{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://course.rs/advance/concurrency-with-threads/intro.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## concurrency-parallelism / 并发和并行\n",
    "\n",
    "- https://course.rs/advance/concurrency-with-threads/concurrency-parallelism.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **并发**：同一时间段内轮流处理多任务；依赖任务切换制造“同时进行”的假象。\n",
    "- **并行**：同一时刻在多核上真正同时运行多任务；并行一定是并发，但并发不必并行。\n",
    "- **单核时代靠并发**：操作系统用线程调度让一个核心快速切换任务，缓解阻塞。\n",
    "- **多核时代可并行**：多个核心可同时执行 N 个任务，同时仍存在更多待处理任务形成并发。\n",
    "- **正式定义**（摘自《并发的艺术》）：并发是动作“同时存在”，并行是动作“同时执行”；并行是并发的子集。\n",
    "- **语言并发模型**：1:1 线程（Rust、C++等直接映射 OS 线程）、M:N 线程/协程（Go、许多运行时时库）、Actor 模型（Erlang）。\n",
    "- **Rust 的取舍**：标准库坚持极小运行时与 1:1 线程，追求系统级性能；若需协程式 M:N 模型，可使用 Tokio 等三方库。\n",
    "\n",
    "掌握这些概念后，即可从“高层策略”过渡到 Rust 实际多线程编程实践。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用线程\n",
    "\n",
    "- https://course.rs/advance/concurrency-with-threads/thread.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **并发风险依旧存在**：线程间执行顺序无法预测，会引发竞态条件、死锁与隐蔽 Bug；Rust 的类型系统虽能降低风险，但不能彻底杜绝。\n",
    "- **线程创建与生命周期**：用 `thread::spawn` 启动线程；主线程结束会立即终止程序，需通过 `JoinHandle::join` 显式等待子线程完成。\n",
    "- **所有权与 `move`**：线程闭包默认借用外部数据，可能悬垂；使用 `move` 把所需数据的所有权转移给新线程，保证内存安全。\n",
    "- **线程退出机制**：Rust 不暴力杀线程；线程代码运行完自然结束。无阻塞的无限循环会长时间占用核心，必须自行设计退出条件。\n",
    "- **性能考量**：线程创建 ≈0.24 ms 开销；CPU 密集任务的线程数通常不宜超过核心数；IO 阻塞场景可多线程，但要留心上下文切换与 OS 限制。\n",
    "- **可伸缩性瓶颈**：即便无锁数据结构，CAS 重试、缓存失效、内存带宽等因素会阻碍线性扩展；读多写少的场景更易扩容。\n",
    "- **同步工具箱**：\n",
    "  - `Barrier` 同步多线程到齐后再继续；\n",
    "  - 线程局部存储可用 `thread_local!` 或第三方 `thread-local`；\n",
    "  - 条件变量 `Condvar` 与 `Mutex` 配合，实现挂起/唤醒；\n",
    "  - `Once::call_once` 保证多线程全局初始化仅执行一次。\n",
    "- **Rust 的选择**：标准库提供 1:1 OS 线程模型、极小运行时；如需 M:N 协程调度，可转向 Tokio 等异步生态。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 多线程编程的风险"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 创建线程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hi number 1 from the main thread!\n",
      "hi number 1 from the spawned thread!\n",
      "hi number 2 from the main thread!\n",
      "hi number 2 from the spawned thread!\n",
      "hi number 3 from the main thread!\n",
      "hi number 3 from the spawned thread!\n",
      "hi number 4 from the main thread!\n",
      "hi number 4 from the spawned thread!\n",
      "hi number 5 from the spawned thread!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    thread::spawn(|| {\n",
    "        for i in 1..10 {\n",
    "            println!(\"hi number {} from the spawned thread!\", i);\n",
    "            thread::sleep(Duration::from_millis(1));\n",
    "        }\n",
    "    });\n",
    "\n",
    "    for i in 1..5 {\n",
    "        println!(\"hi number {} from the main thread!\", i);\n",
    "        thread::sleep(Duration::from_millis(1));\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有几点值得注意：\n",
    "\n",
    "- 线程内部的代码使用闭包来执行\n",
    "- `main` 线程一旦结束，程序就立刻结束，因此需要保持它的存活，直到其它子线程完成自己的任务\n",
    "- `thread::sleep` 会让当前线程休眠指定的时间，随后其它线程会被调度运行（上一节并发与并行中有简单介绍过），因此就算你的电脑只有一个 CPU 核心，该程序也会表现的如同多 CPU 核心一般，这就是并发！\n",
    "\n",
    "\n",
    "如果多运行几次，你会发现好像每次输出会不太一样，因为：虽说线程往往是轮流执行的，但是这一点无法被保证！线程调度的方式往往取决于你使用的操作系统。总之，千万不要依赖线程的执行顺序。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [等待子线程的结束](https://course.rs/advance/concurrency-with-threads/thread.html#等待子线程的结束)\n",
    "\n",
    "上面的代码你不但可能无法让子线程从 1 顺序打印到 10，而且可能打印的数字会变少，因为主线程会提前结束，导致子线程也随之结束，更过分的是，如果当前系统繁忙，甚至该子线程还没被创建，主线程就已经结束了！\n",
    "\n",
    "因此我们需要一个方法，让主线程安全、可靠地等所有子线程完成任务后，再 kill self："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hi number 6 from the spawned thread!\n",
      "hi number 7 from the spawned thread!\n",
      "hi number 8 from the spawned thread!\n",
      "hi number 9 from the spawned thread!\n",
      "hi number 1 from the spawned thread!\n",
      "hi number 2 from the spawned thread!\n",
      "hi number 3 from the spawned thread!\n",
      "hi number 4 from the spawned thread!\n",
      "hi number 1 from the main thread!\n",
      "hi number 2 from the main thread!\n",
      "hi number 3 from the main thread!\n",
      "hi number 4 from the main thread!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let handle = thread::spawn(|| {\n",
    "        for i in 1..5 {\n",
    "            println!(\"hi number {} from the spawned thread!\", i);\n",
    "            thread::sleep(Duration::from_millis(1));\n",
    "        }\n",
    "    });\n",
    "\n",
    "    handle.join().unwrap();\n",
    "\n",
    "    for i in 1..5 {\n",
    "        println!(\"hi number {} from the main thread!\", i);\n",
    "        thread::sleep(Duration::from_millis(1));\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过调用 `handle.join`，可以让当前线程阻塞，直到它等待的子线程的结束，在上面代码中，由于 `main` 线程会被阻塞，因此它直到子线程结束后才会输出自己的 `1..5`：\n",
    "\n",
    "以上输出清晰的展示了线程阻塞的作用，如果你将 `handle.join` 放置在 `main` 线程中的 `for` 循环后面，那就是另外一个结果：两个线程交替输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [在线程闭包中使用 move](https://course.rs/advance/concurrency-with-threads/thread.html#在线程闭包中使用-move)\n",
    "\n",
    "在[闭包](https://course.rs/advance/functional-programing/closure.html#move-和-fn)章节中，有讲过 `move` 关键字在闭包中的使用可以让该闭包拿走环境中某个值的所有权，同样地，你可以使用 `move` 来将所有权从一个线程转移到另外一个线程。\n",
    "\n",
    "首先，来看看在一个线程中直接使用另一个线程中的数据会如何："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "closure may outlive the current function, but it borrows `v`, which is owned by the current function",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0373] Error:\u001b[0m closure may outlive the current function, but it borrows `v`, which is owned by the current function",
      "   \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_4:1:1 \u001b[38;5;246m]\u001b[0m",
      "   \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;68m╭\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m▶\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68md\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68mw\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68m(\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m{\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                                  \u001b[38;5;37m┬\u001b[0m\u001b[38;5;100m┬\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                                  \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword: `move `",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                                   \u001b[38;5;100m│\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                                   \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m may outlive borrowed value `v`",
      " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;68m│\u001b[0m   \u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68ml\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68m!\u001b[0m\u001b[38;5;68m(\u001b[0m\u001b[38;5;68m\"\u001b[0m\u001b[38;5;68mH\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68m'\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68mv\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68mc\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mo\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m?\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;68m\"\u001b[0m\u001b[38;5;68m,\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;68m)\u001b[0m\u001b[38;5;68m;\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                                             \u001b[38;5;54m┬\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                                             \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `v` is borrowed here",
      " \u001b[38;5;246m8 │\u001b[0m \u001b[38;5;68m├\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m▶\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;68m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m             ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m note: function requires argument type to outlive `'static`",
      "\u001b[38;5;246m───╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let v = vec![1, 2, 3];\n",
    "\n",
    "    let handle = thread::spawn(|| {\n",
    "        println!(\"Here's a vector: {:?}\", v);\n",
    "    });\n",
    "\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其实代码本身并没有什么问题，问题在于 Rust 无法确定新的线程会活多久（多个线程的结束顺序并不是固定的），所以也无法确定新线程所引用的 `v` 是否在使用过程中一直合法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let v = vec![1, 2, 3];\n",
    "\n",
    "    let handle = thread::spawn(|| {\n",
    "        println!(\"Here's a vector: {:?}\", v);\n",
    "    });\n",
    "\n",
    "    drop(v); // oh no!\n",
    "\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "大家要记住，线程的启动时间点和结束时间点是不确定的，因此存在一种可能，当主线程执行完， `v` 被释放掉时，新的线程很可能还没有结束甚至还没有被创建成功，此时新线程对 `v` 的引用立刻就不再合法！\n",
    "\n",
    "好在报错里进行了提示：`to force the closure to take ownership of v (and any other referenced variables), use the move keyword`，让我们使用 `move` 关键字拿走 `v` 的所有权即可："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Here's a vector: [1, 2, 3]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let v = vec![1, 2, 3];\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "        println!(\"Here's a vector: {:?}\", v);\n",
    "    });\n",
    "\n",
    "    handle.join().unwrap();\n",
    "\n",
    "    // 下面代码会报错borrow of moved value: `v`\n",
    "    // println!(\"{:?}\",v);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "borrow of moved value: `v`",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0382] Error:\u001b[0m borrow of moved value: `v`",
      "    \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_8:1:1 \u001b[38;5;246m]\u001b[0m",
      "    \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m 4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;37mv\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m[\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m]\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m         \u001b[38;5;37m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m         \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m move occurs because `v` has type `Vec<i32>`, which does not implement the `Copy` trait",
      " \u001b[38;5;240m   │\u001b[0m ",
      " \u001b[38;5;246m 6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m|\u001b[0m\u001b[38;5;54m|\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m                                \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                                   \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m value moved into closure here",
      " \u001b[38;5;246m 7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mH\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m'\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m?\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mv\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m                                           \u001b[38;5;100m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                                           \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m variable moved due to use in closure",
      " \u001b[38;5;240m   │\u001b[0m ",
      " \u001b[38;5;246m12 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m?\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;68mv\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m                     \u001b[38;5;68m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                     \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m value borrowed here after move",
      "\u001b[38;5;246m────╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let v = vec![1, 2, 3];\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "        println!(\"Here's a vector: {:?}\", v);\n",
    "    });\n",
    "\n",
    "    handle.join().unwrap();\n",
    "\n",
    "    println!(\"{:?}\",v);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上所示，很简单的代码，而且 Rust 的所有权机制保证了数据使用上的安全：`v` 的所有权被转移给新的线程后，`main` 线程将无法继续使用：最后一行代码将报错。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [线程是如何结束的](https://course.rs/advance/concurrency-with-threads/thread.html#线程是如何结束的)\n",
    "\n",
    "之前我们提到 `main` 线程是程序的主线程，一旦结束，则程序随之结束，同时各个子线程也将被强行终止。那么有一个问题，如果父线程不是 `main` 线程，那么父线程的结束会导致什么？自生自灭还是被干掉？\n",
    "\n",
    "在系统编程中，操作系统提供了直接杀死线程的接口，简单粗暴，但是 Rust 并没有提供这样的接口，原因在于，粗暴地终止一个线程可能会导致资源没有释放、状态混乱等不可预期的结果，一向以安全自称的 Rust，自然不会砸自己的饭碗。\n",
    "\n",
    "那么 Rust 中线程是如何结束的呢？答案很简单：线程的代码执行完，线程就会自动结束。但是如果线程中的代码不会执行完呢？那么情况可以分为两种进行讨论：\n",
    "\n",
    "- 线程的任务是一个循环 IO 读取，任务流程类似：IO 阻塞，等待读取新的数据 -> 读到数据，处理完成 -> 继续阻塞等待 ··· -> 收到 socket 关闭的信号 -> 结束线程，在此过程中，绝大部分时间线程都处于阻塞的状态，因此虽然看上去是循环，CPU 占用其实很小，也是网络服务中最最常见的模型\n",
    "- 线程的任务是一个循环，里面没有任何阻塞，包括休眠这种操作也没有，此时 CPU 很不幸的会被跑满，而且你如果没有设置终止条件，该线程将持续跑满一个 CPU 核心，并且不会被终止，直到 `main` 线程的结束\n",
    "\n",
    "第一情况很常见，我们来模拟看看第二种情况："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "fn main() {\n",
    "    // 创建一个线程A\n",
    "    let new_thread = thread::spawn(move || {\n",
    "        // 再创建一个线程B\n",
    "        thread::spawn(move || {\n",
    "            loop {\n",
    "                println!(\"I am a new thread.\");\n",
    "            }\n",
    "        })\n",
    "    });\n",
    "\n",
    "    // 等待新创建的线程执行完成\n",
    "    new_thread.join().unwrap();\n",
    "    println!(\"Child thread is finish!\");\n",
    "\n",
    "    // 睡眠一段时间，看子线程创建的子线程是否还在运行\n",
    "    thread::sleep(Duration::from_millis(100));\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上代码中，`main` 线程创建了一个新的线程 `A`，同时该新线程又创建了一个新的线程 `B`，可以看到 `A` 线程在创建完 `B` 线程后就立即结束了，而 `B` 线程则在不停地循环输出。\n",
    "\n",
    "从之前的线程结束规则，我们可以猜测程序将这样执行：`A` 线程结束后，由它创建的 `B` 线程仍在疯狂输出，直到 `main` 线程在 100 毫秒后结束。如果你把该时间增加到几十秒，就可以看到你的 CPU 核心 100% 的盛况了-,-"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [多线程的性能](https://course.rs/advance/concurrency-with-threads/thread.html#多线程的性能)\n",
    "\n",
    "下面我们从多个方面来看看多线程的性能大概是怎么样的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [创建线程的性能](https://course.rs/advance/concurrency-with-threads/thread.html#创建线程的性能)\n",
    "\n",
    "据不精确估算，创建一个线程大概需要 0.24 毫秒，随着线程的变多，这个值会变得更大，因此线程的创建耗时是不可忽略的，只有当真的需要处理一个值得用线程去处理的任务时，才使用线程，一些鸡毛蒜皮的任务，就无需创建线程了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [创建多少线程合适](https://course.rs/advance/concurrency-with-threads/thread.html#创建多少线程合适)\n",
    "\n",
    "因为 CPU 的核心数限制，当任务是 CPU 密集型时，就算线程数超过了 CPU 核心数，也并不能帮你获得更好的性能，因为每个线程的任务都可以轻松让 CPU 的某个核心跑满，既然如此，让线程数等于 CPU 核心数是最好的。\n",
    "\n",
    "但是当你的任务大部分时间都处于阻塞状态时，就可以考虑增多线程数量，这样当某个线程处于阻塞状态时，会被切走，进而运行其它的线程，典型就是网络 IO 操作，我们可以为每一个进来的用户连接创建一个线程去处理，该连接绝大部分时间都是处于 IO 读取阻塞状态，因此有限的 CPU 核心完全可以处理成百上千的用户连接线程，但是事实上，对于这种网络 IO 情况，一般都不再使用多线程的方式了，毕竟操作系统的线程数是有限的，意味着并发数也很容易达到上限，而且过多的线程也会导致线程上下文切换的代价过大，使用 `async/await` 的 `M:N` 并发模型，就没有这个烦恼。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [多线程的开销](https://course.rs/advance/concurrency-with-threads/thread.html#多线程的开销)\n",
    "\n",
    "下面的代码是一个无锁实现(CAS)的 `Hashmap` 在多线程下的使用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "for i in 0..num_threads {\n",
    "    let ht = Arc::clone(&ht);\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "        for j in 0..adds_per_thread {\n",
    "            let key = thread_rng().gen::<u32>();\n",
    "            let value = thread_rng().gen::<u32>();\n",
    "            ht.set_item(key, value);\n",
    "        }\n",
    "    });\n",
    "\n",
    "    handles.push(handle);\n",
    "}\n",
    "\n",
    "for handle in handles {\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "按理来说，既然是无锁实现了，那么锁的开销应该几乎没有，性能会随着线程数的增加接近线性增长，但是真的是这样吗？\n",
    "\n",
    "下图是该代码在 `48` 核机器上的运行结果：\n",
    "\n",
    "![4.6.1](./images/4.6.1.png)\n",
    "\n",
    "从图上可以明显的看出：吞吐并不是线性增长，尤其从 `16` 核开始，甚至开始肉眼可见的下降，这是为什么呢？\n",
    "\n",
    "限于书本的篇幅有限，我们只能给出大概的原因：\n",
    "\n",
    "- 虽然是无锁，但是内部是 CAS 实现，大量线程的同时访问，会让 CAS 重试次数大幅增加\n",
    "- 线程过多时，CPU 缓存的命中率会显著下降，同时多个线程竞争一个 CPU Cache-line 的情况也会经常发生\n",
    "- 大量读写可能会让内存带宽也成为瓶颈\n",
    "- 读和写不一样，无锁数据结构的读往往可以很好地线性增长，但是写不行，因为写竞争太大\n",
    "\n",
    "总之，多线程的开销往往是在锁、数据竞争、缓存失效上，这些限制了现代化软件系统随着 CPU 核心的增多性能也线性增加的野心。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 线程屏障(Barrier)\n",
    "\n",
    "在 Rust 中，可以使用 `Barrier` 让多个线程都执行到某个点后，才继续一起往后执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "before wait\n",
      "before wait\n",
      "before wait\n",
      "before wait\n",
      "before wait\n",
      "before wait\n",
      "after wait\n",
      "after wait\n",
      "after wait\n",
      "after wait\n",
      "after wait\n",
      "after wait\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::{Arc, Barrier};\n",
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let mut handles = Vec::with_capacity(6);\n",
    "    let barrier = Arc::new(Barrier::new(6));\n",
    "\n",
    "    for _ in 0..6 {\n",
    "        let b = barrier.clone();\n",
    "        handles.push(thread::spawn(move|| {\n",
    "            println!(\"before wait\");\n",
    "            b.wait();\n",
    "            println!(\"after wait\");\n",
    "        }));\n",
    "    }\n",
    "\n",
    "    for handle in handles {\n",
    "        handle.join().unwrap();\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码，我们在线程打印出 `before wait` 后增加了一个屏障，目的就是等所有的线程都打印出**before wait**后，各个线程再继续执行："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [线程局部变量(Thread Local Variable)](https://course.rs/advance/concurrency-with-threads/thread.html#线程局部变量thread-local-variable)\n",
    "\n",
    "对于多线程编程，线程局部变量在一些场景下非常有用，而 Rust 通过标准库和三方库对此进行了支持。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [标准库 thread_local](https://course.rs/advance/concurrency-with-threads/thread.html#标准库-thread_local)\n",
    "\n",
    "使用 `thread_local` 宏可以初始化线程局部变量，然后在线程内部使用该变量的 `with` 方法获取变量值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::cell::RefCell;\n",
    "use std::thread;\n",
    "\n",
    "thread_local!(static FOO: RefCell<u32> = RefCell::new(1));\n",
    "\n",
    "FOO.with(|f| {\n",
    "    assert_eq!(*f.borrow(), 1);\n",
    "    *f.borrow_mut() = 2;\n",
    "});\n",
    "\n",
    "// 每个线程开始时都会拿到线程局部变量的FOO的初始值\n",
    "let t = thread::spawn(move|| {\n",
    "    FOO.with(|f| {\n",
    "        assert_eq!(*f.borrow(), 1);\n",
    "        *f.borrow_mut() = 3;\n",
    "    });\n",
    "});\n",
    "\n",
    "// 等待线程完成\n",
    "t.join().unwrap();\n",
    "\n",
    "// 尽管子线程中修改为了3，我们在这里依然拥有main线程中的局部值：2\n",
    "FOO.with(|f| {\n",
    "    assert_eq!(*f.borrow(), 2);\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码中，`FOO` 即是我们创建的**线程局部变量**，每个新的线程访问它时，都会使用它的初始值作为开始，各个线程中的 `FOO` 值彼此互不干扰。注意 `FOO` 使用 `static` 声明为生命周期为 `'static` 的静态变量。\n",
    "\n",
    "可以注意到，线程中对 `FOO` 的使用是通过借用的方式，但是若我们需要每个线程独自获取它的拷贝，最后进行汇总，就有些强人所难了。\n",
    "\n",
    "你还可以在结构体中使用线程局部变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RefCell { value: 0 }\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::cell::RefCell;\n",
    "\n",
    "struct Foo;\n",
    "impl Foo {\n",
    "    thread_local! {\n",
    "        static FOO: RefCell<usize> = RefCell::new(0);\n",
    "    }\n",
    "}\n",
    "\n",
    "{\n",
    "    Foo::FOO.with(|x| println!(\"{:?}\", x));\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者通过引用的方式使用它:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::cell::RefCell;\n",
    "use std::thread::LocalKey;\n",
    "\n",
    "thread_local! {\n",
    "    static FOO: RefCell<usize> = RefCell::new(0);\n",
    "}\n",
    "struct Bar {\n",
    "    foo: &'static LocalKey<RefCell<usize>>,\n",
    "}\n",
    "impl Bar {\n",
    "    fn constructor() -> Self {\n",
    "        Self {\n",
    "            foo: &FOO,\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [三方库 thread-local](https://course.rs/advance/concurrency-with-threads/thread.html#三方库-thread-local)\n",
    "\n",
    "除了标准库外，一位大神还开发了 [thread-local](https://github.com/Amanieu/thread_local-rs) 库，它允许每个线程持有值的独立拷贝："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use thread_local::ThreadLocal;\n",
    "use std::sync::Arc;\n",
    "use std::cell::Cell;\n",
    "use std::thread;\n",
    "\n",
    "let tls = Arc::new(ThreadLocal::new());\n",
    "let mut v = vec![];\n",
    "// 创建多个线程\n",
    "for _ in 0..5 {\n",
    "    let tls2 = tls.clone();\n",
    "    let handle = thread::spawn(move || {\n",
    "        // 将计数器加1\n",
    "        // 请注意，由于线程 ID 在线程退出时会被回收，因此一个线程有可能回收另一个线程的对象\n",
    "        // 这只能在线程退出后发生，因此不会导致任何竞争条件\n",
    "        let cell = tls2.get_or(|| Cell::new(0));\n",
    "        cell.set(cell.get() + 1);\n",
    "    });\n",
    "    v.push(handle);\n",
    "}\n",
    "for handle in v {\n",
    "    handle.join().unwrap();\n",
    "}\n",
    "// 一旦所有子线程结束，收集它们的线程局部变量中的计数器值，然后进行求和\n",
    "let tls = Arc::try_unwrap(tls).unwrap();\n",
    "let total = tls.into_iter().fold(0, |x, y| {\n",
    "    // 打印每个线程局部变量中的计数器值，发现不一定有5个线程，\n",
    "    // 因为一些线程已退出，并且其他线程会回收退出线程的对象\n",
    "    println!(\"x: {}, y: {}\", x, y.get());\n",
    "    x + y.get()\n",
    "});\n",
    "\n",
    "// 和为5\n",
    "assert_eq!(total, 5);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "该库不仅仅使用了值的拷贝，而且还能自动把多个拷贝汇总到一个迭代器中，最后进行求和，非常好用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 用条件控制线程的挂起和执行\n",
    "\n",
    "条件变量(Condition Variables)经常和 `Mutex` 一起使用，可以让线程挂起，直到某个条件发生后再继续执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::thread;\n",
    "use std::sync::{Arc, Mutex, Condvar};\n",
    "\n",
    "{\n",
    "    let pair = Arc::new((Mutex::new(false), Condvar::new()));\n",
    "    let pair2 = pair.clone();\n",
    "\n",
    "    thread::spawn(move|| {\n",
    "        let (lock, cvar) = &*pair2;\n",
    "        let mut started = lock.lock().unwrap();\n",
    "        println!(\"changing started\");\n",
    "        *started = true;\n",
    "        cvar.notify_one();\n",
    "    });\n",
    "\n",
    "    let (lock, cvar) = &*pair;\n",
    "    let mut started = lock.lock().unwrap();\n",
    "    while !*started {\n",
    "        started = cvar.wait(started).unwrap();\n",
    "    }\n",
    "\n",
    "    println!(\"started changed\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上述代码流程如下：\n",
    "\n",
    "1. `main` 线程首先进入 `while` 循环，调用 `wait` 方法挂起等待子线程的通知，并释放了锁 `started`\n",
    "2. 子线程获取到锁，并将其修改为 `true`，然后调用条件变量的 `notify_one` 方法来通知主线程继续执行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [只被调用一次的函数](https://course.rs/advance/concurrency-with-threads/thread.html#只被调用一次的函数)\n",
    "\n",
    "有时，我们会需要某个函数在多线程环境下只被调用一次，例如初始化全局变量，无论是哪个线程先调用函数来初始化，都会保证全局变量只会被初始化一次，随后的其它线程调用就会忽略该函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::thread;\n",
    "use std::sync::Once;\n",
    "\n",
    "static mut VAL: usize = 0;\n",
    "static INIT: Once = Once::new();\n",
    "\n",
    "{\n",
    "    let handle1 = thread::spawn(move || {\n",
    "        INIT.call_once(|| {\n",
    "            unsafe {\n",
    "                VAL = 1;\n",
    "            }\n",
    "        });\n",
    "    });\n",
    "\n",
    "    let handle2 = thread::spawn(move || {\n",
    "        INIT.call_once(|| {\n",
    "            unsafe {\n",
    "                VAL = 2;\n",
    "            }\n",
    "        });\n",
    "    });\n",
    "\n",
    "    handle1.join().unwrap();\n",
    "    handle2.join().unwrap();\n",
    "\n",
    "    println!(\"{}\", unsafe { VAL });\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代码运行的结果取决于哪个线程先调用 `INIT.call_once` （虽然代码具有先后顺序，但是线程的初始化顺序并无法被保证！因为线程初始化是异步的，且耗时较久），若 `handle1` 先，则输出 `1`，否则输出 `2`。\n",
    "\n",
    "**call_once 方法**\n",
    "\n",
    "执行初始化过程一次，并且只执行一次。\n",
    "\n",
    "如果当前有另一个初始化过程正在运行，线程将阻止该方法被调用。\n",
    "\n",
    "当这个函数返回时，保证一些初始化已经运行并完成，它还保证由执行的闭包所执行的任何内存写入都能被其他线程在这时可靠地观察到。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [总结](https://course.rs/advance/concurrency-with-threads/thread.html#总结)\n",
    "\n",
    "[Rust 的线程模型](https://course.rs/advance/concurrency-with-threads/intro.html)是 `1:1` 模型，因为 Rust 要保持尽量小的运行时。\n",
    "\n",
    "我们可以使用 `thread::spawn` 来创建线程，创建出的多个线程之间并不存在执行顺序关系，因此代码逻辑千万不要依赖于线程间的执行顺序。\n",
    "\n",
    "`main` 线程若是结束，则所有子线程都将被终止，如果希望等待子线程结束后，再结束 `main` 线程，你需要使用创建线程时返回的句柄的 `join` 方法。\n",
    "\n",
    "在线程中无法直接借用外部环境中的变量值，因为新线程的启动时间点和结束时间点是不确定的，所以 Rust 无法保证该线程中借用的变量在使用过程中依然是合法的。你可以使用 `move` 关键字将变量的所有权转移给新的线程，来解决此问题。\n",
    "\n",
    "父线程结束后，子线程仍在持续运行，直到子线程的代码运行完成或者 `main` 线程的结束。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 线程同步：消息传递\n",
    "\n",
    "- https://course.rs/advance/concurrency-with-threads/message-passing.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **Channel 基本模型**：`std::sync::mpsc` 支持 *多生产者单消费者*；发送端 `Sender<T>` / 接收端 `Receiver<T>` 一旦类型确定，只能传递同一种类型。\n",
    "- **阻塞与非阻塞接收**：`recv()` 阻塞直到取到值或通道关闭；`try_recv()` 立即返回，若无消息报 `Err(Empty)`，若所有发送端关闭报 `Err(Disconnected)`。\n",
    "- **所有权规则**：发送 `Copy` 类型时复制数值；非 `Copy` 类型（如 `String`）发送会移动所有权，发送端无法再使用该值，保障线程安全。\n",
    "- **循环消费**：`for received in rx` 会逐条阻塞读取；当所有发送端被 drop、通道关闭后自动退出循环。\n",
    "- **多发送者并发**：通过 `Sender::clone()` 为每个线程复制一份发送端；只有所有发送者都 drop，接收端迭代才会结束。\n",
    "- **FIFO 保序**：同一发送端的消息按发送顺序被接收；线程启动先后不确定，但通道内部保证先进先出。\n",
    "- **同步 vs 异步通道**：`channel()` 创建无界异步通道，发送不阻塞；`sync_channel(cap)` 创建有界同步通道，缓冲未满时可无阻塞发送（`cap=0` 退化为完全同步），缓冲满则发送方阻塞，适合控制内存占用。\n",
    "- **通道关闭条件**：所有发送端或所有接收端 drop 后，通道自动关闭；避免阻塞需手动 `drop(send)` 清理最后一个发送者。\n",
    "- **多类型消息**：可用枚举统一多种数据类型，但需注意枚举按照最大成员大小对齐，可能浪费内存。\n",
    "- **踩坑警示**：别忘在主线程 drop 原始 `Sender`；否则接收端遍历不会结束，主线程会卡住。\n",
    "- **第三方库拓展**：需要多生产者多消费者或更高性能时考虑 `crossbeam-channel`、`flume` 等库，提供更丰富的模型和表现。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在多线程间有多种方式可以共享、传递数据，最常用的方式就是通过消息传递或者将锁和`Arc`联合使用，而对于前者，在编程界还有一个大名鼎鼎的`Actor线程模型`为其背书，典型的有 Erlang 语言，还有 Go 语言中很经典的一句话：\n",
    "\n",
    "> Do not communicate by sharing memory; instead, share memory by communicating\n",
    "\n",
    "而对于后者，我们将在下一节中进行讲述。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [消息通道](https://course.rs/advance/concurrency-with-threads/message-passing.html#消息通道)\n",
    "\n",
    "与 Go 语言内置的`chan`不同，Rust 是在标准库里提供了消息通道(`channel`)，你可以将其想象成一场直播，多个主播联合起来在搞一场直播，最终内容通过通道传输给屏幕前的我们，其中主播被称之为**发送者**，观众被称之为**接收者**，显而易见的是：一个通道应该支持多个发送者和接收者。\n",
    "\n",
    "但是，在实际使用中，我们需要使用不同的库来满足诸如：**多发送者 -> 单接收者，多发送者 -> 多接收者**等场景形式，此时一个标准库显然就不够了，不过别急，让我们先从标准库讲起。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [多发送者，单接收者](https://course.rs/advance/concurrency-with-threads/message-passing.html#多发送者单接收者)\n",
    "\n",
    "标准库提供了通道`std::sync::mpsc`，其中`mpsc`是*multiple producer, single consumer*的缩写，代表了该通道支持多个发送者，但是只支持唯一的接收者。 当然，支持多个发送者也意味着支持单个发送者，我们先来看看单发送者、单接收者的简单例子:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "receive 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "\n",
    "{\n",
    "    // 创建一个消息通道, 返回一个元组：(发送者，接收者)\n",
    "    let (tx, rx) = mpsc::channel();\n",
    "\n",
    "    // 创建线程，并发送消息\n",
    "    thread::spawn(move || {\n",
    "        // 发送一个数字1, send方法返回Result<T,E>，通过unwrap进行快速错误处理\n",
    "        tx.send(1).unwrap();\n",
    "\n",
    "        // 下面代码将报错，因为编译器自动推导出通道传递的值是i32类型，那么Option<i32>类型将产生不匹配错误\n",
    "        // tx.send(Some(1)).unwrap()\n",
    "    });\n",
    "\n",
    "    // 在主线程中接收子线程发送的消息并输出\n",
    "    println!(\"receive {}\", rx.recv().unwrap());\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上代码并不复杂，但仍有几点需要注意：\n",
    "\n",
    "- `tx`,`rx`对应发送者和接收者，它们的类型由编译器自动推导: `tx.send(1)`发送了整数，因此它们分别是`mpsc::Sender<i32>`和`mpsc::Receiver<i32>`类型，需要注意，由于内部是泛型实现，一旦类型被推导确定，该通道就只能传递对应类型的值, 例如此例中非`i32`类型的值将导致编译错误\n",
    "- 接收消息的操作`rx.recv()`会阻塞当前线程，直到读取到值，或者通道被关闭\n",
    "- 需要使用`move`将`tx`的所有权转移到子线程的闭包中\n",
    "\n",
    "在注释中提到`send`方法返回一个`Result<T,E>`，说明它有可能返回一个错误，例如接收者被`drop`导致了发送的值不会被任何人接收，此时继续发送毫无意义，因此返回一个错误最为合适，在代码中我们仅仅使用`unwrap`进行了快速处理，但在实际项目中你需要对错误进行进一步的处理。\n",
    "\n",
    "同样的，对于`recv`方法来说，当发送者关闭时，它也会接收到一个错误，用于说明不会再有任何值被发送过来。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [不阻塞的 try_recv 方法](https://course.rs/advance/concurrency-with-threads/message-passing.html#不阻塞的-try_recv-方法)\n",
    "\n",
    "除了上述`recv`方法，还可以使用`try_recv`尝试接收一次消息，该方法并**不会阻塞线程**，当通道中没有消息时，它会立刻返回一个错误："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "receive Err(Empty)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "thread '<unnamed>' panicked at src/lib.rs:38:20:\n",
      "called `Result::unwrap()` on an `Err` value: SendError { .. }\n",
      "stack backtrace:\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "   0: __rustc::rust_begin_unwind\n",
      "             at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panicking.rs:697:5\n",
      "   1: core::panicking::panic_fmt\n",
      "             at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/panicking.rs:75:14\n",
      "   2: core::result::unwrap_failed\n",
      "             at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/result.rs:1765:5\n",
      "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n"
     ]
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let (tx, rx) = mpsc::channel();\n",
    "\n",
    "    thread::spawn(move || {\n",
    "        tx.send(1).unwrap();\n",
    "    });\n",
    "\n",
    "    println!(\"receive {:?}\", rx.try_recv());\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于子线程的创建需要时间，因此`println!`和`try_recv`方法会先执行，而此时子线程的**消息还未被发出**。`try_recv`会尝试立即读取一次消息，因为消息没有发出，此次读取最终会报错，且主线程运行结束(可悲的是，相对于主线程中的代码，子线程的创建速度实在是过慢，直到主线程结束，都无法完成子线程的初始化。。):`receive Err(Empty)`\n",
    "\n",
    "如上，`try_recv`返回了一个错误，错误内容是`Empty`，代表通道并没有消息。如果你尝试把`println!`复制一些行，就会发现一个有趣的输出："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "receive Err(Empty)\n",
      "receive Ok(1)\n",
      "receive Err(Disconnected)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let (tx, rx) = mpsc::channel();\n",
    "\n",
    "    thread::spawn(move || {\n",
    "        tx.send(1).unwrap();\n",
    "    });\n",
    "\n",
    "    println!(\"receive {:?}\", rx.try_recv());\n",
    "    thread::sleep(Duration::from_millis(100));\n",
    "    println!(\"receive {:?}\", rx.try_recv());\n",
    "    println!(\"receive {:?}\", rx.try_recv());\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上，当子线程创建成功且发送消息后，主线程会接收到`Ok(1)`的消息内容，紧接着子线程结束，发送者也随着被`drop`，此时接收者又会报错，但是这次错误原因有所不同：`Disconnected`代表发送者已经被关闭。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [传输具有所有权的数据](https://course.rs/advance/concurrency-with-threads/message-passing.html#传输具有所有权的数据)\n",
    "\n",
    "使用通道来传输数据，一样要遵循 Rust 的所有权规则：\n",
    "\n",
    "- 若值的类型实现了`Copy`特征，则直接复制一份该值，然后传输过去，例如之前的`i32`类型\n",
    "- 若值没有实现`Copy`，则它的所有权会被转移给接收端，在发送端继续使用该值将报错\n",
    "\n",
    "一起来看看第二种情况:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "borrow of moved value: `s`",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0382] Error:\u001b[0m borrow of moved value: `s`",
      "    \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_18:1:1 \u001b[38;5;246m]\u001b[0m",
      "    \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m 8 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mI\u001b[0m\u001b[38;5;249m'\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m             \u001b[38;5;68m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m             \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m move occurs because `s` has type `String`, which does not implement the `Copy` trait",
      " \u001b[38;5;246m 9 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m                 \u001b[38;5;54m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                 \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m value moved here",
      " \u001b[38;5;246m10 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m                               \u001b[38;5;100m┬\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m                               \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m value borrowed here after move",
      "\u001b[38;5;246m────╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let (tx, rx) = mpsc::channel();\n",
    "\n",
    "    thread::spawn(move || {\n",
    "        let s = String::from(\"I'm flying!\");\n",
    "        tx.send(s).unwrap();\n",
    "        println!(\"val is {}\", s);\n",
    "    });\n",
    "\n",
    "    let received = rx.recv().unwrap();\n",
    "    println!(\"Got: {}\", received);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上代码中，`String`底层的字符串是存储在堆上，并没有实现`Copy`特征，当它被发送后，会将所有权从发送端的`s`转移给接收端的`received`，之后`s`将无法被使用:\n",
    "\n",
    "各种细节不禁令人感叹：Rust 还是安全！假如没有所有权的保护，`String`字符串将被两个线程同时持有，任何一个线程对字符串内容的修改都会导致另外一个线程持有的字符串被改变，除非你故意这么设计，否则这就是不安全的隐患。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [使用 for 进行循环接收](https://course.rs/advance/concurrency-with-threads/message-passing.html#使用-for-进行循环接收)\n",
    "\n",
    "下面来看看如何连续接收通道中的值:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Got: hi\n",
      "Got: from\n",
      "Got: the\n",
      "Got: thread\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let (tx, rx) = mpsc::channel();\n",
    "\n",
    "    thread::spawn(move || {\n",
    "        let vals = vec![\n",
    "            String::from(\"hi\"),\n",
    "            String::from(\"from\"),\n",
    "            String::from(\"the\"),\n",
    "            String::from(\"thread\"),\n",
    "        ];\n",
    "\n",
    "        for val in vals {\n",
    "            tx.send(val).unwrap();\n",
    "            thread::sleep(Duration::from_secs(1));\n",
    "        }\n",
    "    });\n",
    "\n",
    "    for received in rx {\n",
    "        println!(\"Got: {}\", received);\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上面代码中，主线程和子线程是并发运行的，子线程在不停的**发送消息 -> 休眠 1 秒**，与此同时，主线程使用`for`循环**阻塞**的从`rx`**迭代器**中接收消息，当子线程运行完成时，发送者`tx`会随之被`drop`，此时`for`循环将被终止，最终`main`线程成功结束。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [使用多发送者](https://course.rs/advance/concurrency-with-threads/message-passing.html#使用多发送者)\n",
    "\n",
    "由于子线程会拿走发送者的所有权，因此我们必须对发送者进行克隆，然后让每个线程拿走它的一份拷贝:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Got: hi from raw tx\n",
      "Got: hi from cloned tx\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "\n",
    "{\n",
    "    let (tx, rx) = mpsc::channel();\n",
    "    let tx1 = tx.clone();\n",
    "    thread::spawn(move || {\n",
    "        tx.send(String::from(\"hi from raw tx\")).unwrap();\n",
    "    });\n",
    "\n",
    "    thread::spawn(move || {\n",
    "        tx1.send(String::from(\"hi from cloned tx\")).unwrap();\n",
    "    });\n",
    "\n",
    "    for received in rx {\n",
    "        println!(\"Got: {}\", received);\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "代码并无太大区别，就多了一个对发送者的克隆`let tx1 = tx.clone();`，然后一个子线程拿走`tx`的所有权，另一个子线程拿走`tx1`的所有权，皆大欢喜。\n",
    "\n",
    "但是有几点需要注意:\n",
    "\n",
    "- 需要所有的发送者都被`drop`掉后，接收者`rx`才会收到错误，进而跳出`for`循环，最终结束主线程\n",
    "- 这里虽然用了`clone`但是并不会影响性能，因为它并不在热点代码路径中，仅仅会被执行一次\n",
    "- 由于两个子线程谁先创建完成是未知的，因此哪条消息先发送也是未知的，最终主线程的输出顺序也不确定"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [消息顺序](https://course.rs/advance/concurrency-with-threads/message-passing.html#消息顺序)\n",
    "\n",
    "上述第三点的消息顺序仅仅是因为线程创建引起的，并不代表通道中的消息是无序的，对于通道而言，消息的发送顺序和接收顺序是一致的，满足`FIFO`原则(先进先出)。\n",
    "\n",
    "由于篇幅有限，具体的代码这里就不再给出，感兴趣的读者可以自己验证下。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [同步和异步通道](https://course.rs/advance/concurrency-with-threads/message-passing.html#同步和异步通道)\n",
    "\n",
    "Rust 标准库的`mpsc`通道其实分为两种类型：同步和异步。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [异步通道](https://course.rs/advance/concurrency-with-threads/message-passing.html#异步通道)\n",
    "\n",
    "之前我们使用的都是异步通道：无论接收者是否正在接收消息，消息发送者在发送消息时都不会阻塞:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "睡眠之前\n",
      "发送之前\n",
      "发送之后\n",
      "睡眠之后\n",
      "receive 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let (tx, rx)= mpsc::channel();\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "        println!(\"发送之前\");\n",
    "        tx.send(1).unwrap();\n",
    "        println!(\"发送之后\");\n",
    "    });\n",
    "\n",
    "    println!(\"睡眠之前\");\n",
    "    thread::sleep(Duration::from_secs(3));\n",
    "    println!(\"睡眠之后\");\n",
    "\n",
    "    println!(\"receive {}\", rx.recv().unwrap());\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "主线程因为睡眠阻塞了 3 秒，因此并没有进行消息接收，而子线程却在此期间轻松完成了消息的发送。等主线程睡眠结束后，才姗姗来迟的从通道中接收了子线程老早之前发送的消息。\n",
    "\n",
    "从输出还可以看出，`发送之前`和`发送之后`是连续输出的，没有受到接收端主线程的任何影响，因此通过`mpsc::channel`创建的通道是异步通道。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [同步通道](https://course.rs/advance/concurrency-with-threads/message-passing.html#同步通道)\n",
    "\n",
    "与异步通道相反，同步通道**发送消息是阻塞的，只有在消息被接收后才解除阻塞**，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "睡眠之前\n",
      "发送之前\n",
      "睡眠之后\n",
      "receive 1\n",
      "发送之后\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let (tx, rx)= mpsc::sync_channel(0);\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "        println!(\"发送之前\");\n",
    "        tx.send(1).unwrap();\n",
    "        println!(\"发送之后\");\n",
    "    });\n",
    "\n",
    "    println!(\"睡眠之前\");\n",
    "    thread::sleep(Duration::from_secs(3));\n",
    "    println!(\"睡眠之后\");\n",
    "\n",
    "    println!(\"receive {}\", rx.recv().unwrap());\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看出，主线程由于睡眠被阻塞导致无法接收消息，因此子线程的发送也一直被阻塞，直到主线程结束睡眠并成功接收消息后，发送才成功：**发送之后**的输出是在**receive 1**之后，说明**只有接收消息彻底成功后，发送消息才算完成**。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [消息缓存](https://course.rs/advance/concurrency-with-threads/message-passing.html#消息缓存)\n",
    "\n",
    "细心的读者可能已经发现在创建同步通道时，我们传递了一个参数`0`: `mpsc::sync_channel(0);`，这是什么意思呢？\n",
    "\n",
    "答案不急给出，先将`0`改成`1`，然后再运行试试:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "睡眠之前\n",
      "发送之前\n",
      "发送之后\n",
      "睡眠之后\n",
      "receive 1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let (tx, rx)= mpsc::sync_channel(1);\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "        println!(\"发送之前\");\n",
    "        tx.send(1).unwrap();\n",
    "        println!(\"发送之后\");\n",
    "    });\n",
    "\n",
    "    println!(\"睡眠之前\");\n",
    "    thread::sleep(Duration::from_secs(3));\n",
    "    println!(\"睡眠之后\");\n",
    "\n",
    "    println!(\"receive {}\", rx.recv().unwrap());\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "纳尼。。竟然得到了和异步通道一样的效果：根本没有等待主线程的接收开始，消息发送就立即完成了！ 难道同步通道变成了异步通道？ 别急，将子线程中的代码修改下试试："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "睡眠之前\n",
      "首次发送之前\n",
      "首次发送之后\n",
      "睡眠之后\n",
      "receive 1\n",
      "再次发送之后\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc;\n",
    "use std::thread;\n",
    "use std::time::Duration;\n",
    "\n",
    "{\n",
    "    let (tx, rx)= mpsc::sync_channel(1);\n",
    "\n",
    "    let handle = thread::spawn(move || {\n",
    "      println!(\"首次发送之前\");\n",
    "      tx.send(1).unwrap();\n",
    "      println!(\"首次发送之后\");\n",
    "      tx.send(1).unwrap();\n",
    "      println!(\"再次发送之后\");\n",
    "    });\n",
    "\n",
    "    println!(\"睡眠之前\");\n",
    "    thread::sleep(Duration::from_secs(3));\n",
    "    println!(\"睡眠之后\");\n",
    "\n",
    "    println!(\"receive {}\", rx.recv().unwrap());\n",
    "    handle.join().unwrap();\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Bingo，更奇怪的事出现了，第一条消息瞬间发送完成，没有阻塞，而发送第二条消息时却符合同步通道的特点：阻塞了，直到主线程接收后，才发送完成。\n",
    "\n",
    "其实，一切的关键就在于`1`上，该值可以用来指定同步通道的消息缓存条数，当你设定为`N`时，发送者就可以无阻塞的往通道中发送`N`条消息，当消息缓冲队列满了后，新的消息发送将被阻塞(如果没有接收者消费缓冲队列中的消息，那么第`N+1`条消息就将触发发送阻塞)。\n",
    "\n",
    "问题又来了，异步通道创建时完全没有这个缓冲值参数`mpsc::channel()`，它的缓冲值怎么设置呢？ 额。。。都异步了，都可以无限发送了，都有摩托车了，还要自行车做啥子哦？事实上异步通道的缓冲上限取决于你的内存大小，不要撑爆就行。\n",
    "\n",
    "因此，使用异步消息虽然能非常高效且不会造成发送线程的阻塞，但是存在消息未及时消费，最终内存过大的问题。在实际项目中，可以考虑使用一个带缓冲值的同步通道来避免这种风险。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [关闭通道](https://course.rs/advance/concurrency-with-threads/message-passing.html#关闭通道)\n",
    "\n",
    "之前我们数次提到了通道关闭，并且提到了当通道关闭后，发送消息或接收消息将会报错。那么如何关闭通道呢？ 很简单：**所有发送者被`drop`或者所有接收者被`drop`后，通道会自动关闭**。\n",
    "\n",
    "神奇的是，这件事是在编译期实现的，完全没有运行期性能损耗！只能说 Rust 的`Drop`特征 YYDS!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [传输多种类型的数据](https://course.rs/advance/concurrency-with-threads/message-passing.html#传输多种类型的数据)\n",
    "\n",
    "之前提到过，一个消息通道只能传输一种类型的数据，如果你想要传输多种类型的数据，可以为每个类型创建一个通道，你也可以使用枚举类型来实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "received sweet oranges\n",
      "received 2 apples\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::mpsc::{self, Receiver, Sender};\n",
    "\n",
    "enum Fruit {\n",
    "    Apple(u8),\n",
    "    Orange(String)\n",
    "}\n",
    "\n",
    "{\n",
    "    let (tx, rx): (Sender<Fruit>, Receiver<Fruit>) = mpsc::channel();\n",
    "\n",
    "    tx.send(Fruit::Orange(\"sweet\".to_string())).unwrap();\n",
    "    tx.send(Fruit::Apple(2)).unwrap();\n",
    "\n",
    "    for _ in 0..2 {\n",
    "        match rx.recv().unwrap() {\n",
    "            Fruit::Apple(count) => println!(\"received {} apples\", count),\n",
    "            Fruit::Orange(flavor) => println!(\"received {} oranges\", flavor),\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如上所示，枚举类型还能让我们带上想要传输的数据，但是有一点需要注意，Rust 会按照枚举中占用内存最大的那个成员进行内存对齐，这意味着就算你传输的是枚举中占用内存最小的成员，它占用的内存依然和最大的成员相同, 因此会造成内存上的浪费。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [新手容易遇到的坑](https://course.rs/advance/concurrency-with-threads/message-passing.html#新手容易遇到的坑)\n",
    "\n",
    "`mpsc`虽然相当简洁明了，但是在使用起来还是可能存在坑："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::mpsc;\n",
    "\n",
    "fn main() {\n",
    "\n",
    "    use std::thread;\n",
    "\n",
    "    let (send, recv) = mpsc::channel();\n",
    "    let num_threads = 3;\n",
    "    for i in 0..num_threads {\n",
    "        let thread_send = send.clone();\n",
    "        thread::spawn(move || {\n",
    "            thread_send.send(i).unwrap();\n",
    "            println!(\"thread {:?} finished\", i);\n",
    "        });\n",
    "    }\n",
    "\n",
    "    // 在这里drop send...\n",
    "    // drop(send);\n",
    "\n",
    "    for x in recv {\n",
    "        println!(\"Got: {}\", x);\n",
    "    }\n",
    "    println!(\"finished iterating\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上代码看起来非常正常，但是运行后主线程会一直阻塞，最后一行打印输出也不会被执行，原因在于： 子线程拿走的是复制后的`send`的所有权，这些拷贝会在子线程结束后被`drop`，因此无需担心，但是`send`本身却直到`main`函数的结束才会被`drop`。\n",
    "\n",
    "之前提到，通道关闭的两个条件：发送者全部`drop`或接收者被`drop`，要结束`for`循环显然是要求发送者全部`drop`，但是由于`send`自身没有被`drop`，会导致该循环永远无法结束，最终主线程会一直阻塞。\n",
    "\n",
    "解决办法很简单，`drop`掉`send`即可：在代码中的注释下面添加一行`drop(send);`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [mpmc 更好的性能](https://course.rs/advance/concurrency-with-threads/message-passing.html#mpmc-更好的性能)\n",
    "\n",
    "如果你需要 mpmc(多发送者，多接收者)或者需要更高的性能，可以考虑第三方库:\n",
    "\n",
    "- [**crossbeam-channel**](https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel), 老牌强库，功能较全，性能较强，之前是独立的库，但是后面合并到了`crossbeam`主仓库中\n",
    "- [**flume**](https://github.com/zesterer/flume), 官方给出的性能数据某些场景要比 crossbeam 更好些"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 线程同步：锁、Condvar 和信号量\n",
    "\n",
    "- https://course.rs/advance/concurrency-with-threads/sync1.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **互斥锁 `Mutex<T>`**：串行化访问共享资源；`MutexGuard` 自动解锁，注意作用域与 `drop`；配合 `Arc<T>` 实现多线程内部可变性；`try_lock` 可避免阻塞；警惕死锁。\n",
    "- **读写锁 `RwLock<T>`**：允许多读单写，读写互斥；`read`/`write` 与 `try_read`/`try_write`；读多且操作耗时才值得使用，否则 `Mutex` 更简单高效。\n",
    "- **条件变量 `Condvar`**：与 `Mutex` 配合挂起/唤醒线程，等待共享状态达成；`wait` 会暂时释放锁，醒来后重新获取；使用 `notify_one`/`notify_all` 通知。\n",
    "- **信号量 `Semaphore`**：控制并发上限，适合限制资源/任务并发数；推荐 `tokio::sync::Semaphore`；获取许可 (`acquire`) → 执行任务 → 释放许可 (`drop`).\n",
    "\n",
    "\n",
    "| 并发原语           | 核心特性                                                     | 注意事项 / 常见陷阱                                          | 常用搭配                               | 典型使用场景                                     |\n",
    "| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------------------------------------ |\n",
    "| 互斥锁 `Mutex<T>`  | `Mutex::new` 包裹共享数据；`lock()` 阻塞获取 `MutexGuard`，`Drop` 自动解锁；`try_lock()` 非阻塞尝试 | 持锁线程 `panic` 会导致 `PoisonError`；需及时释放锁（控制作用域或手动 `drop`）；防止单线程重复 `lock()` 或多线程交叉持锁形成死锁 | `Arc<Mutex<T>>` 用于多线程内部可变性   | 简单共享数据的互斥访问；写多场景；优先默认选择   |\n",
    "| 读写锁 `RwLock<T>` | 多读（`read()`）同时成功；写（`write()`）独占；`try_read()` / `try_write()` 非阻塞尝试 | 写饥饿风险；实现复杂且性能 < `Mutex`；读写不能同时发生       | `Arc<RwLock<T>>`                       | 读多写少且读操作耗时较长，如大对象统计、缓存回收 |\n",
    "| 条件变量 `Condvar` | `wait(guard)` 释放锁并阻塞，唤醒后重新持锁返回；`notify_one()` / `notify_all()` 唤醒等待线程 | 需配合 `while` 循环二次检查条件以防虚假唤醒；务必与 `Mutex` 搭配使用 | `Arc<Mutex<State>>` + `Arc<Condvar>`   | 线程间事件/状态同步，如生产者-消费者节奏控制     |\n",
    "| 信号量 `Semaphore` | 控制活动任务数量：获取许可 (`acquire`) → 执行 → 释放 (`drop`)；`tokio::sync::Semaphore` 推荐 | 未释放许可会导致等待线程永久阻塞；标准库旧实现已弃用         | `Arc<Semaphore>`、异步运行时 (`tokio`) | 限制并发任务/资源上限，例如连接池、批量任务限流  |\n",
    "| 第三方锁实现       | `parking_lot`：API 友好、性能佳；`spin`：自旋锁，某些场景稍快 | `spin` 维护较少；选型前建议基准测试验证收益                  | ——                                     | 对性能/功能有更高要求时替代标准库锁实现          |\n",
    "\n",
    "**选型指南**\n",
    "\n",
    "| 场景                       | 推荐原语               |\n",
    "| -------------------------- | ---------------------- |\n",
    "| 简单共享数据，读写都少     | `Arc<Mutex<T>>`        |\n",
    "| 高频并发读、读操作长、写少 | `Arc<RwLock<T>>`       |\n",
    "| 需要等待状态变化/事件      | `Mutex + Condvar`      |\n",
    "| 控制任务并发数             | `Semaphore`            |\n",
    "| 大量跨线程消息流转         | `channel`（异步/同步） |\n",
    "\n",
    "> 不确定用哪种锁？从 `Mutex` 起步，衡量性能/并发需求后再切换。基准测试是消除猜测的最佳方式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在多线程编程中，同步性极其的重要，当你需要同时访问一个资源、控制不同线程的执行次序时，都需要使用到同步性。\n",
    "\n",
    "在 Rust 中有多种方式可以实现同步性。在上一节中讲到的消息传递就是同步性的一种实现方式，例如我们可以通过消息传递来控制不同线程间的执行次序。还可以使用共享内存来实现同步性，例如通过锁和原子操作等并发原语来实现多个线程同时且安全地去访问一个资源。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [该如何选择](https://course.rs/advance/concurrency-with-threads/sync1.html#该如何选择)\n",
    "\n",
    "共享内存可以说是同步的灵魂，因为消息传递的底层实际上也是通过共享内存来实现，两者的区别如下：\n",
    "\n",
    "- 共享内存相对消息传递能节省多次内存拷贝的成本\n",
    "- 共享内存的实现简洁的多\n",
    "- 共享内存的锁竞争更多\n",
    "\n",
    "消息传递适用的场景很多，我们下面列出了几个主要的使用场景:\n",
    "\n",
    "- 需要可靠和简单的(简单不等于简洁)实现时\n",
    "- 需要模拟现实世界，例如用消息去通知某个目标执行相应的操作时\n",
    "- 需要一个任务处理流水线(管道)时，等等\n",
    "\n",
    "而使用共享内存(并发原语)的场景往往就比较简单粗暴：需要简洁的实现以及更高的性能时。\n",
    "\n",
    "总之，消息传递类似一个单所有权的系统：一个值同时只能有一个所有者，如果另一个线程需要该值的所有权，需要将所有权通过消息传递进行转移。而共享内存类似于一个多所有权的系统：多个线程可以同时访问同一个值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [互斥锁 Mutex](https://course.rs/advance/concurrency-with-threads/sync1.html#互斥锁-mutex)\n",
    "\n",
    "既然是共享内存，那并发原语自然是重中之重，先来一起看看皇冠上的明珠: 互斥锁`Mutex`(mutual exclusion 的缩写)。\n",
    "\n",
    "`Mutex`让多个线程并发的访问同一个值变成了排队访问：同一时间，只允许一个线程`A`访问该值，其它线程需要等待`A`访问完成后才能继续。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [单线程中使用 Mutex](https://course.rs/advance/concurrency-with-threads/sync1.html#单线程中使用-mutex)\n",
    "\n",
    "先来看看单线程中`Mutex`该如何使用:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "m = Mutex { data: 6, poisoned: false, .. }\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "use std::sync::Mutex;\n",
    "\n",
    "{\n",
    "    // 使用`Mutex`结构体的关联函数创建新的互斥锁实例\n",
    "    let m = Mutex::new(5);\n",
    "\n",
    "    {\n",
    "        // 获取锁，然后deref为`m`的引用\n",
    "        // lock返回的是Result\n",
    "        let mut num = m.lock().unwrap();\n",
    "        *num = 6;\n",
    "        // 锁自动被drop\n",
    "    }\n",
    "\n",
    "    println!(\"m = {:?}\", m);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在注释中，已经大致描述了代码的功能，不过有一点需要注意：和`Box`类似，数据被`Mutex`所拥有，要访问内部的数据，需要使用方法`m.lock()`向`m`申请一个锁, 该方法会**阻塞当前线程，直到获取到锁**，因此当多个线程同时访问该数据时，只有一个线程能获取到锁，其它线程只能阻塞着等待，这样就保证了数据能被安全的修改！\n",
    "\n",
    "**`m.lock()`方法也有可能报错**，例如当前正在持有锁的线程`panic`了。在这种情况下，其它线程不可能再获得锁，因此`lock`方法会返回一个错误。\n",
    "\n",
    "这里你可能奇怪，`m.lock`明明返回一个锁，怎么就变成我们的`num`数值了？聪明的读者可能会想到智能指针，没错，因为`Mutex<T>`是一个智能指针，准确的说是`m.lock()`返回一个智能指针`MutexGuard<T>`:\n",
    "\n",
    "- 它实现了`Deref`特征，会被自动解引用后获得一个引用类型，该引用指向`Mutex`内部的数据\n",
    "- 它还实现了`Drop`特征，在超出作用域后，自动释放锁，以便其它线程能继续获取锁\n",
    "\n",
    "正因为智能指针的使用，使得我们无需任何操作就能获取其中的数据。 如果释放锁，你需要做的仅仅是做好锁的作用域管理，例如上述代码的内部花括号使用，建议读者尝试下去掉内部的花括号，然后再次尝试获取第二个锁`num1`，看看会发生什么，友情提示：不会报错，但是主线程会永远阻塞，因为不幸发生了死锁。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::Mutex;\n",
    "\n",
    "fn main() {\n",
    "    let m = Mutex::new(5);\n",
    "\n",
    "    let mut num = m.lock().unwrap();\n",
    "    *num = 6;\n",
    "    // 锁还没有被 drop 就尝试申请下一个锁，导致主线程阻塞\n",
    "    // drop(num); // 手动 drop num ，可以让 num1 申请到下个锁\n",
    "    let mut num1 = m.lock().unwrap();\n",
    "    *num1 = 7;\n",
    "    // drop(num1); // 手动 drop num1 ，观察打印结果的不同\n",
    "\n",
    "    println!(\"m = {:?}\", m);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [多线程中使用 Mutex](https://course.rs/advance/concurrency-with-threads/sync1.html#多线程中使用-mutex)\n",
    "\n",
    "单线程中使用锁，说实话纯粹是为了演示功能，毕竟多线程才是锁的舞台。 现在，我们再来看看，如何在多线程下使用`Mutex`来访问同一个资源."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [无法运行的`Rc`](https://course.rs/advance/concurrency-with-threads/sync1.html#无法运行的rct)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "`Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0277] Error:\u001b[0m `Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely",
      "    \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_29:1:1 \u001b[38;5;246m]\u001b[0m",
      "    \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m13 │\u001b[0m \u001b[38;5;54m╭\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68md\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68mw\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;100mm\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mv\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m{\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                        \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                              \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m required by a bound introduced by this call",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                                         \u001b[38;5;100m│\u001b[0m     ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                                         \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m within this `{closure@src/lib.rs:32:36: 32:43}`",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                                         \u001b[38;5;37m│\u001b[0m     ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                                         \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m note: required because it's used within this closure",
      " \u001b[38;5;240m   ┆\u001b[0m \u001b[38;5;54m┆\u001b[0m   ",
      " \u001b[38;5;246m17 │\u001b[0m \u001b[38;5;54m├\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                 ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely",
      "\u001b[38;5;246m────╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "// https://godbolt.org/z/sjo1Yxcen\n",
    "\n",
    "use std::rc::Rc;\n",
    "use std::sync::Mutex;\n",
    "use std::thread;\n",
    "\n",
    "fn main() {\n",
    "    // Use `Rc` to share ownership of the `Mutex`\n",
    "    let counter = Rc::new(Mutex::new(0));\n",
    "    let mut handles = vec![];\n",
    "\n",
    "    for _ in 0..10 {\n",
    "        let counter = Rc::clone(&counter);\n",
    "        // Spawn a child thread and move a clone of the `Mutex` owner into it\n",
    "        let handle = thread::spawn(move || {\n",
    "            let mut num = counter.lock().unwrap();\n",
    "\n",
    "            *num += 1;\n",
    "        });\n",
    "        handles.push(handle);\n",
    "    }\n",
    "\n",
    "    // Wait for all child threads to finish\n",
    "    for handle in handles {\n",
    "        handle.join().unwrap();\n",
    "    }\n",
    "\n",
    "    // Print the final count\n",
    "    println!(\"Result: {}\", *counter.lock().unwrap());\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于子线程需要通过`move`拿走锁的所有权，因此我们需要使用多所有权来保证每个线程都拿到数据的独立所有权，恰好智能指针[`Rc`](https://course.rs/advance/smart-pointer/rc-arc.html)可以做到(**上面代码会报错**！具体往下看，别跳过-, -)。\n",
    "\n",
    "以上代码实现了在多线程中计数的功能，由于多个线程都需要去修改该计数器，因此我们需要使用锁来保证同一时间只有一个线程可以修改计数器，否则会导致脏数据：想象一下 A 线程和 B 线程同时拿到计数器，获取了当前值`1`, 并且同时对其进行了修改，最后值变成`2`，你会不会在风中凌乱？毕竟正确的值是`3`，因为两个线程各自加 1。\n",
    "\n",
    "可能有人会说，有那么巧的事情吗？事实上，对于人类来说，因为干啥啥慢，并没有那么多巧合，所以人总会存在巧合心理。但是对于计算机而言，每秒可以轻松运行上亿次，在这种频次下，一切巧合几乎都将必然发生，因此千万不要有任何侥幸心理。\n",
    "\n",
    "> 如果事情有变坏的可能，不管这种可能性有多小，它都会发生！ - 在计算机领域歪打正着的墨菲定律\n",
    "\n",
    "事实上，上面的代码会报错:\n",
    "\n",
    "错误中提到了一个关键点：`Rc<T>`无法在线程中传输，因为它没有实现`Send`特征(在下一节将详细介绍)，而该特征可以确保数据在线程中安全的传输。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [多线程安全的 `Arc`](https://course.rs/advance/concurrency-with-threads/sync1.html#多线程安全的-arct)\n",
    "\n",
    "好在，我们有`Arc<T>`，得益于它的[内部计数器](https://course.rs/advance/smart-pointer/rc-arc.html#多线程无力的rc)是多线程安全的，因此可以在多线程环境中使用:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Result: 10\n"
     ]
    }
   ],
   "source": [
    "use std::sync::{Arc, Mutex};\n",
    "use std::thread;\n",
    "\n",
    "fn main() {\n",
    "    let counter = Arc::new(Mutex::new(0));\n",
    "    let mut handles = vec![];\n",
    "\n",
    "    for _ in 0..10 {\n",
    "        let counter = Arc::clone(&counter);\n",
    "        let handle = thread::spawn(move || {\n",
    "            let mut num = counter.lock().unwrap();\n",
    "\n",
    "            *num += 1;\n",
    "        });\n",
    "        handles.push(handle);\n",
    "    }\n",
    "\n",
    "    for handle in handles {\n",
    "        handle.join().unwrap();\n",
    "    }\n",
    "\n",
    "    println!(\"Result: {}\", *counter.lock().unwrap());\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [内部可变性](https://course.rs/advance/concurrency-with-threads/sync1.html#内部可变性)\n",
    "\n",
    "在之前章节，我们提到过[内部可变性](https://course.rs/advance/smart-pointer/cell-refcell.html#内部可变性)，其中`Rc<T>`和`RefCell<T>`的结合，可以实现单线程的内部可变性。\n",
    "\n",
    "现在我们又有了新的武器，由于`Mutex<T>`可以支持修改内部数据，当结合`Arc<T>`一起使用时，可以实现多线程的内部可变性。\n",
    "\n",
    "简单总结下：`Rc<T>/RefCell<T>`用于单线程内部可变性， `Arc<T>/Mutex<T>`用于多线程内部可变性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [需要小心使用的 Mutex](https://course.rs/advance/concurrency-with-threads/sync1.html#需要小心使用的-mutex)\n",
    "\n",
    "如果有其它语言的编程经验，就知道互斥锁这家伙不好对付，想要正确使用，你得牢记在心：\n",
    "\n",
    "- 在使用数据前必须先获取锁\n",
    "- 在数据使用完成后，必须**及时**的释放锁，比如文章开头的例子，使用内部语句块的目的就是为了及时的释放锁\n",
    "\n",
    "这两点看起来不起眼，但要正确的使用，其实是相当不简单的，对于其它语言，忘记释放锁是经常发生的，虽然 Rust 通过智能指针的`drop`机制帮助我们避免了这一点，但是由于不及时释放锁导致的性能问题也是常见的。\n",
    "\n",
    "正因为这种困难性，导致很多用户都热衷于使用消息传递的方式来实现同步，例如 Go 语言直接把`channel`内置在语言特性中，甚至还有无锁的语言，例如`erlang`，完全使用`Actor`模型，依赖消息传递来完成共享和同步。幸好 Rust 的类型系统、所有权机制、智能指针等可以很好的帮助我们减轻使用锁时的负担。\n",
    "\n",
    "另一个值的注意的是在使用`Mutex<T>`时，Rust 无法帮我们避免所有的逻辑错误，例如在之前章节，我们提到过使用`Rc<T>`可能会导致[循环引用的问题](https://course.rs/advance/circle-self-ref/circle-reference.html)。类似的，`Mutex<T>`也存在使用上的风险，例如创建死锁(deadlock)：当一个操作试图锁住两个资源，然后两个线程各自获取其中一个锁，并试图获取另一个锁时，就会造成死锁。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [死锁](https://course.rs/advance/concurrency-with-threads/sync1.html#死锁)\n",
    "\n",
    "在 Rust 中有多种方式可以创建死锁，了解这些方式有助于你提前规避可能的风险，一起来看看。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [单线程死锁](https://course.rs/advance/concurrency-with-threads/sync1.html#单线程死锁)\n",
    "\n",
    "这种死锁比较容易规避，但是当代码复杂后还是有可能遇到："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::Mutex;\n",
    "\n",
    "fn main() {\n",
    "    let data = Mutex::new(0);\n",
    "    let d1 = data.lock();\n",
    "    let d2 = data.lock();\n",
    "} // d1锁在此处释放"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "非常简单，只要你在另一个锁还未被释放时去申请新的锁，就会触发，当代码复杂后，这种情况可能就没有那么显眼。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [多线程死锁](https://course.rs/advance/concurrency-with-threads/sync1.html#多线程死锁)\n",
    "\n",
    "当我们拥有两个锁，且两个线程各自使用了其中一个锁，然后试图去访问另一个锁时，就可能发生死锁："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::{sync::{Mutex, MutexGuard}, thread};\n",
    "use std::thread::sleep;\n",
    "use std::time::Duration;\n",
    "\n",
    "use lazy_static::lazy_static;\n",
    "lazy_static! {\n",
    "    static ref MUTEX1: Mutex<i64> = Mutex::new(0);\n",
    "    static ref MUTEX2: Mutex<i64> = Mutex::new(0);\n",
    "}\n",
    "\n",
    "fn main() {\n",
    "    // 存放子线程的句柄\n",
    "    let mut children = vec![];\n",
    "    for i_thread in 0..2 {\n",
    "        children.push(thread::spawn(move || {\n",
    "            for _ in 0..1 {\n",
    "                // 线程1\n",
    "                if i_thread % 2 == 0 {\n",
    "                    // 锁住MUTEX1\n",
    "                    let guard: MutexGuard<i64> = MUTEX1.lock().unwrap();\n",
    "\n",
    "                    println!(\"线程 {} 锁住了MUTEX1，接着准备去锁MUTEX2 !\", i_thread);\n",
    "\n",
    "                    // 当前线程睡眠一小会儿，等待线程2锁住MUTEX2\n",
    "                    sleep(Duration::from_millis(10));\n",
    "\n",
    "                    // 去锁MUTEX2\n",
    "                    let guard = MUTEX2.lock().unwrap();\n",
    "                // 线程2\n",
    "                } else {\n",
    "                    // 锁住MUTEX2\n",
    "                    let _guard = MUTEX2.lock().unwrap();\n",
    "\n",
    "                    println!(\"线程 {} 锁住了MUTEX2, 准备去锁MUTEX1\", i_thread);\n",
    "\n",
    "                    let _guard = MUTEX1.lock().unwrap();\n",
    "                }\n",
    "            }\n",
    "        }));\n",
    "    }\n",
    "\n",
    "    // 等子线程完成\n",
    "    for child in children {\n",
    "        let _ = child.join();\n",
    "    }\n",
    "\n",
    "    println!(\"死锁没有发生\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上面的描述中，我们用了\"可能\"二字，原因在于死锁在这段代码中不是必然发生的，总有一次运行你能看到最后一行打印输出。这是由于子线程的初始化顺序和执行速度并不确定，我们无法确定哪个线程中的锁先被执行，因此也无法确定两个线程对锁的具体使用顺序。\n",
    "\n",
    "但是，可以简单的说明下死锁发生的必然条件：线程 1 锁住了`MUTEX1`并且线程`2`锁住了`MUTEX2`，然后线程 1 试图去访问`MUTEX2`，同时线程`2`试图去访问`MUTEX1`，就会死锁。 因为线程 2 需要等待线程 1 释放`MUTEX1`后，才会释放`MUTEX2`，而与此同时，线程 1 需要等待线程 2 释放`MUTEX2`后才能释放`MUTEX1`，这种情况造成了两个线程都无法释放对方需要的锁，最终死锁。\n",
    "\n",
    "那么为何某些时候，死锁不会发生？原因很简单，线程 2 在线程 1 锁`MUTEX1`之前，就已经全部执行完了，随之线程 2 的`MUTEX2`和`MUTEX1`被全部释放，线程 1 对锁的获取将不再有竞争者。 同理，线程 1 若全部被执行完，那线程 2 也不会被锁，因此我们在线程 1 中间加一个睡眠，增加死锁发生的概率。如果你在线程 2 中同样的位置也增加一个睡眠，那死锁将必然发生!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [try_lock](https://course.rs/advance/concurrency-with-threads/sync1.html#try_lock)\n",
    "\n",
    "与`lock`方法不同，`try_lock`会**尝试**去获取一次锁，如果无法获取会返回一个错误，因此**不会发生阻塞**:\n",
    "\n",
    "- [main.rs](./try_lock/src/main.rs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "线程 0 锁住了MUTEX1，接着准备去锁MUTEX2 !\n",
       "线程 1 锁住了MUTEX2, 准备去锁MUTEX1\n",
       "线程 0 获取 MUTEX2 锁的结果: Err(\"WouldBlock\")\n",
       "线程 1 获取 MUTEX1 锁的结果: Err(\"WouldBlock\")\n",
       "死锁没有发生\n",
       "warning: unused variable: `guard`\n",
       "  --> src/main.rs:20:25\n",
       "   |\n",
       "20 |                     let guard: MutexGuard<i64> = MUTEX1.lock().unwrap();\n",
       "   |                         ^^^^^ help: if this is intentional, prefix it with an underscore: `_guard`\n",
       "   |\n",
       "   = note: `#[warn(unused_variables)]` on by default\n",
       "\n"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "! cd try_lock && cargo run -q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了演示`try_lock`的作用，我们特定使用了之前必定会死锁的代码，并且将`lock`替换成`try_lock`，与之前的结果不同，这段代码将不会再有死锁发生：\n",
    "\n",
    "如上所示，当`try_lock`失败时，会报出一个错误:`Err(\"WouldBlock\")`，接着线程中的剩余代码会继续执行，不会被阻塞。\n",
    "\n",
    "> 一个有趣的命名规则：在 Rust 标准库中，使用`try_xxx`都会尝试进行一次操作，如果无法完成，就立即返回，不会发生阻塞。例如消息传递章节中的`try_recv`以及本章节中的`try_lock`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [读写锁 RwLock](https://course.rs/advance/concurrency-with-threads/sync1.html#读写锁-rwlock)\n",
    "\n",
    "`Mutex`会对每次读写都进行加锁，但某些时候，我们需要大量的并发读，`Mutex`就无法满足需求了，此时就可以使用`RwLock`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::RwLock;\n",
    "\n",
    "fn main() {\n",
    "    let lock = RwLock::new(5);\n",
    "\n",
    "    // 同一时间允许多个读\n",
    "    {\n",
    "        let r1 = lock.read().unwrap();\n",
    "        let r2 = lock.read().unwrap();\n",
    "        assert_eq!(*r1, 5);\n",
    "        assert_eq!(*r2, 5);\n",
    "    } // 读锁在此处被drop\n",
    "\n",
    "    // 同一时间只允许一个写\n",
    "    {\n",
    "        let mut w = lock.write().unwrap();\n",
    "        *w += 1;\n",
    "        assert_eq!(*w, 6);\n",
    "\n",
    "        // 以下代码会阻塞发生死锁，因为读和写不允许同时存在\n",
    "        // 写锁w直到该语句块结束才被释放，因此下面的读锁依然处于`w`的作用域中\n",
    "        // let r1 = lock.read();\n",
    "        // println!(\"{:?}\",r1);\n",
    "    }// 写锁在此处被drop\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`RwLock`在使用上和`Mutex`区别不大，只有在多个读的情况下不阻塞程序，其他如读写、写读、写写情况下均会对后获取锁的操作进行阻塞。\n",
    "\n",
    "我们也可以使用`try_write`和`try_read`来尝试进行一次写/读，若失败则返回错误:`Err(\"WouldBlock\")`\n",
    "\n",
    "简单总结下`RwLock`:\n",
    "\n",
    "1. 同时允许多个读，但最多只能有一个写\n",
    "2. 读和写不能同时存在\n",
    "3. 读可以使用`read`、`try_read`，写`write`、`try_write`, 在实际项目中，`try_xxx`会安全的多"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [Mutex 还是 RwLock](https://course.rs/advance/concurrency-with-threads/sync1.html#mutex-还是-rwlock)\n",
    "\n",
    "首先简单性上`Mutex`完胜，因为使用`RwLock`你得操心几个问题：\n",
    "\n",
    "- 读和写不能同时发生，如果使用`try_xxx`解决，就必须做大量的错误处理和失败重试机制\n",
    "- 当读多写少时，写操作可能会因为一直无法获得锁导致连续多次失败([writer starvation](https://stackoverflow.com/questions/2190090/how-to-prevent-writer-starvation-in-a-read-write-lock-in-pthreads))\n",
    "- RwLock 其实是操作系统提供的，实现原理要比`Mutex`复杂的多，因此单就锁的性能而言，比不上原生实现的`Mutex`\n",
    "\n",
    "再来简单总结下两者的使用场景：\n",
    "\n",
    "- 追求高并发读取时，使用`RwLock`，因为`Mutex`一次只允许一个线程去读取\n",
    "- 如果要保证写操作的成功性，使用`Mutex`\n",
    "- 不知道哪个合适，统一使用`Mutex`\n",
    "\n",
    "需要注意的是，`RwLock`虽然看上去貌似提供了高并发读取的能力，但这个不能说明它的性能比`Mutex`高，事实上`Mutex`性能要好不少，后者**唯一的问题也仅仅在于不能并发读取**。\n",
    "\n",
    "一个常见的、错误的使用`RwLock`的场景就是使用`HashMap`进行简单读写，因为`HashMap`的读和写都非常快，`RwLock`的复杂实现和相对低的性能反而会导致整体性能的降低，因此一般来说更适合使用`Mutex`。\n",
    "\n",
    "总之，如果你要使用`RwLock`要确保满足以下两个条件：**并发读，且需要对读到的资源进行\"长时间\"的操作**，`HashMap`也许满足了并发读的需求，但是往往并不能满足后者：\"长时间\"的操作。\n",
    "\n",
    "> benchmark 永远是你在迷茫时最好的朋友！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [三方库提供的锁实现](https://course.rs/advance/concurrency-with-threads/sync1.html#三方库提供的锁实现)\n",
    "\n",
    "标准库在设计时总会存在取舍，因为往往性能并不是最好的，如果你追求性能，可以使用三方库提供的并发原语:\n",
    "\n",
    "- [parking_lot](https://crates.io/crates/parking_lot), 功能更完善、稳定，社区较为活跃，star 较多，更新较为活跃\n",
    "- [spin](https://crates.io/crates/spin), 在多数场景中性能比`parking_lot`高一点，最近没怎么更新\n",
    "\n",
    "如果不是追求特别极致的性能，建议选择前者。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [用条件变量(Condvar)控制线程的同步](https://course.rs/advance/concurrency-with-threads/sync1.html#用条件变量condvar控制线程的同步)\n",
    "\n",
    "`Mutex`用于解决资源安全访问的问题，但是我们还需要一个手段来解决资源访问顺序的问题。而 Rust 考虑到了这一点，为我们提供了条件变量(Condition Variables)，它经常和`Mutex`一起使用，可以让线程挂起，直到某个条件发生后再继续执行，其实`Condvar`我们在之前的多线程章节就已经见到过，现在再来看一个不同的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "outside counter: 1\n",
      "inner counter: 1\n",
      "outside counter: 2\n",
      "inner counter: 2\n",
      "outside counter: 3\n",
      "inner counter: 3\n",
      "Mutex { data: true, poisoned: false, .. }\n"
     ]
    }
   ],
   "source": [
    "use std::sync::{Arc,Mutex,Condvar};\n",
    "use std::thread::{spawn,sleep};\n",
    "use std::time::Duration;\n",
    "\n",
    "fn main() {\n",
    "    let flag = Arc::new(Mutex::new(false));\n",
    "    let cond = Arc::new(Condvar::new());\n",
    "    let cflag = flag.clone();\n",
    "    let ccond = cond.clone();\n",
    "\n",
    "    let hdl = spawn(move || {\n",
    "        let mut lock = cflag.lock().unwrap();\n",
    "        let mut counter = 0;\n",
    "\n",
    "        while counter < 3 {\n",
    "            while !*lock {\n",
    "                // wait方法会接收一个MutexGuard<'a, T>，且它会自动地暂时释放这个锁，使其他线程可以拿到锁并进行数据更新。\n",
    "                // 同时当前线程在此处会被阻塞，直到被其他地方notify后，它会将原本的MutexGuard<'a, T>还给我们，即重新获取到了锁，同时唤醒了此线程。\n",
    "                lock = ccond.wait(lock).unwrap();\n",
    "            }\n",
    "            \n",
    "            *lock = false;\n",
    "\n",
    "            counter += 1;\n",
    "            println!(\"inner counter: {}\", counter);\n",
    "        }\n",
    "    });\n",
    "\n",
    "    let mut counter = 0;\n",
    "    loop {\n",
    "        sleep(Duration::from_millis(1000));\n",
    "        *flag.lock().unwrap() = true;\n",
    "        counter += 1;\n",
    "        if counter > 3 {\n",
    "            break;\n",
    "        }\n",
    "        println!(\"outside counter: {}\", counter);\n",
    "        cond.notify_one();\n",
    "    }\n",
    "    hdl.join().unwrap();\n",
    "    println!(\"{:?}\", flag);\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [信号量 Semaphore](https://course.rs/advance/concurrency-with-threads/sync1.html#信号量-semaphore)\n",
    "\n",
    "在多线程中，另一个重要的概念就是信号量，使用它可以让我们精准的控制当前正在运行的任务最大数量。想象一下，当一个新游戏刚开服时(有些较火的老游戏也会，比如`wow`)，往往会控制游戏内玩家的同时在线数，一旦超过某个临界值，就开始进行排队进服。而在实际使用中，也有很多时候，我们需要通过信号量来控制最大并发数，防止服务器资源被撑爆。\n",
    "\n",
    "本来 Rust 在标准库中有提供一个[信号量实现](https://doc.rust-lang.org/1.8.0/std/sync/struct.Semaphore.html), 但是由于各种原因这个库现在已经不再推荐使用了，因此我们推荐使用`tokio`中提供的`Semaphore`实现: [`tokio::sync::Semaphore`](https://github.com/tokio-rs/tokio/blob/master/tokio/src/sync/semaphore.rs)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::Arc;\n",
    "use tokio::sync::Semaphore;\n",
    "\n",
    "#[tokio::main]\n",
    "async fn main() {\n",
    "    let semaphore = Arc::new(Semaphore::new(3));\n",
    "    let mut join_handles = Vec::new();\n",
    "\n",
    "    for _ in 0..5 {\n",
    "        let permit = semaphore.clone().acquire_owned().await.unwrap();\n",
    "        join_handles.push(tokio::spawn(async move {\n",
    "            //\n",
    "            // 在这里执行任务...\n",
    "            //\n",
    "            drop(permit);\n",
    "        }));\n",
    "    }\n",
    "\n",
    "    for handle in join_handles {\n",
    "        handle.await.unwrap();\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码创建了一个容量为 3 的信号量，当正在执行的任务超过 3 时，剩下的任务需要等待正在执行任务完成并减少信号量后到 3 以内时，才能继续执行。\n",
    "\n",
    "这里的关键其实说白了就在于：信号量的申请和归还，使用前需要申请信号量，如果容量满了，就需要等待；使用后需要释放信号量，以便其它等待者可以继续。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [总结](https://course.rs/advance/concurrency-with-threads/sync1.html#总结)\n",
    "\n",
    "在很多时候，消息传递都是非常好用的手段，它可以让我们的数据在任务流水线上不断流转，实现起来非常优雅。\n",
    "\n",
    "但是它并不能优雅的解决所有问题，因为我们面临的真实世界是非常复杂的，无法用某一种银弹统一解决。当面临消息传递不太适用的场景时，或者需要更好的性能和简洁性时，我们往往需要用锁来解决这些问题，因为锁允许多个线程同时访问同一个资源，简单粗暴。\n",
    "\n",
    "除了锁之外，其实还有一种并发原语可以帮助我们解决并发访问数据的问题，那就是原子类型 Atomic，在下一章节中，我们会对其进行深入讲解。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 线程同步：Atomic 原子类型与内存顺序\n",
    "- https://course.rs/advance/concurrency-with-threads/sync2.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **原子=无锁高性能**：CPU 指令级保证读改写一致，避免显式加锁/解锁，冲突多时仍需等待（CAS 循环）。\n",
    "- **全局计数首选**：`static Atomic*` + `fetch_add` 等操作，简洁且比 `Mutex` 快 ~40%+，仍具内部可变性。\n",
    "- **内存顺序 5 选 1**：\n",
    "  - `Relaxed`：只保证原子本身正确，完全不约束重排。\n",
    "  - `Release` / `Acquire`：成对使用，形成内存屏障，常见“发布-订阅”模型。\n",
    "  - `AcqRel`：单个原子操作同时写+读。\n",
    "  - `SeqCst`：最严、最安全，优先默认选它。\n",
    "- **仍不可替代锁**：Atomic 仅支持标量类型；复杂共享状态、条件变量等仍需 `Mutex`/`Condvar`。\n",
    "- **典型用法**：`Arc<Atomic*>` 搭配跨线程；自旋等待 `spin_loop`；构建无锁结构、全局 ID、指标计数器等。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Mutex`用起来简单，但是无法并发读，`RwLock`可以并发读，但是使用场景较为受限且性能不够，那么有没有一种全能性选手呢？ 欢迎我们的`Atomic`闪亮登场。\n",
    "\n",
    "从 Rust1.34 版本后，就正式支持原子类型。原子指的是一系列不可被 CPU 上下文交换的机器指令，这些指令组合在一起就形成了原子操作。在多核 CPU 下，当某个 CPU 核心开始运行原子操作时，会先暂停其它 CPU 内核对内存的操作，以保证原子操作不会被其它 CPU 内核所干扰。\n",
    "\n",
    "由于原子操作是通过指令提供的支持，因此它的性能相比锁和消息传递会好很多。相比较于锁而言，原子类型不需要开发者处理加锁和释放锁的问题，同时支持修改，读取等操作，还具备较高的并发性能，几乎所有的语言都支持原子类型。\n",
    "\n",
    "可以看出原子类型是无锁类型，但是无锁不代表无需等待，因为原子类型内部使用了`CAS`循环，当大量的冲突发生时，该等待还是得[等待](https://course.rs/advance/concurrency-with-threads/thread.html#多线程的开销)！但是总归比锁要好。\n",
    "\n",
    "> CAS 全称是 Compare and swap, 它通过一条指令读取指定的内存地址，然后判断其中的值是否等于给定的前置值，如果相等，则将其修改为新的值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [使用 Atomic 作为全局变量](https://course.rs/advance/concurrency-with-threads/sync2.html#使用-atomic-作为全局变量)\n",
    "\n",
    "原子类型的一个常用场景，就是作为全局变量来使用:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\n",
       "running 0 tests\n",
       "\n",
       "test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
       "\n",
       "atomic counter          time:   [819.50 ms 831.89 ms 844.35 ms]\n",
       "                        change: [+2.1119% +4.0291% +6.0262%] (p = 0.00 < 0.05)\n",
       "                        Performance has regressed.\n",
       "\n",
       "mutex counter           time:   [2.7459 s 2.7642 s 2.7830 s]\n",
       "                        change: [−2.5796% −1.0670% +0.5706%] (p = 0.23 > 0.05)\n",
       "                        No change in performance detected.\n",
       "\n",
       "warning: variable does not need to be mutable\n",
       "  --> benches/compare.rs:17:9\n",
       "   |\n",
       "17 |     let mut crit = Criterion::default()\n",
       "   |         ----^^^^\n",
       "   |         |\n",
       "   |         help: remove this `mut`\n",
       "   |\n",
       "   = note: `#[warn(unused_mut)]` on by default\n",
       "\n",
       "Gnuplot not found, using plotters backend\n",
       "Benchmarking atomic counter\n",
       "Benchmarking atomic counter: Warming up for 1.0000 s\n",
       "\n",
       "Warning: Unable to complete 10 samples in 2.0s. You may wish to increase target time to 8.1s.\n",
       "Benchmarking atomic counter: Collecting 10 samples in estimated 8.0516 s (10 iterations)\n",
       "Benchmarking atomic counter: Analyzing\n",
       "Benchmarking mutex counter\n",
       "Benchmarking mutex counter: Warming up for 1.0000 s\n",
       "\n",
       "Warning: Unable to complete 10 samples in 2.0s. You may wish to increase target time to 26.6s.\n",
       "Benchmarking mutex counter: Collecting 10 samples in estimated 26.650 s (10 iterations)\n",
       "Benchmarking mutex counter: Analyzing\n"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "! cd atomic_mutex && cargo bench -q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上代码启动了数个线程，每个线程都在疯狂对全局变量进行加 1 操作, 最后将它与`线程数 * 加1次数`进行比较，如果发生了因为多个线程同时修改导致了脏数据，那么这两个必将不相等。好在，它没有让我们失望，不仅快速的完成了任务，而且保证了 100%的并发安全性。\n",
    "\n",
    "当然以上代码的功能其实也可以通过`Mutex`来实现，但是后者的强大功能是建立在额外的性能损耗基础上的，因此性能会逊色不少:\n",
    "\n",
    "可以看到`Atomic`实现会比`Mutex`快**41%**，实际上在复杂场景下还能更快(甚至达到 4 倍的性能差距)！\n",
    "\n",
    "还有一点值得注意: **和`Mutex`一样，`Atomic`的值具有内部可变性**，你无需将其声明为`mut`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::Mutex;\n",
    "use std::sync::atomic::{Ordering, AtomicU64};\n",
    "\n",
    "struct Counter {\n",
    "    count: u64\n",
    "}\n",
    "\n",
    "fn main() {\n",
    "    let n = Mutex::new(Counter {\n",
    "        count: 0\n",
    "    });\n",
    "\n",
    "    n.lock().unwrap().count += 1;\n",
    "\n",
    "    let n = AtomicU64::new(0);\n",
    "\n",
    "    n.fetch_add(0, Ordering::Relaxed);\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里有一个奇怪的枚举成员`Ordering::Relaxed`, 看上去很像是排序作用，但是我们并没有做排序操作啊？实际上它用于控制原子操作使用的**内存顺序**。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [内存顺序](https://course.rs/advance/concurrency-with-threads/sync2.html#内存顺序)\n",
    "\n",
    "内存顺序是指 CPU 在访问内存时的顺序，该顺序可能受以下因素的影响：\n",
    "\n",
    "- 代码中的先后顺序\n",
    "- 编译器优化导致在编译阶段发生改变(内存重排序 reordering)\n",
    "- 运行阶段因 CPU 的缓存机制导致顺序被打乱"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [编译器优化导致内存顺序的改变](https://course.rs/advance/concurrency-with-threads/sync2.html#编译器优化导致内存顺序的改变)\n",
    "\n",
    "对于第二点，我们举个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "static mut X: u64 = 0;\n",
    "static mut Y: u64 = 1;\n",
    "\n",
    "fn main() {\n",
    "    ...     // A\n",
    "\n",
    "    unsafe {\n",
    "        ... // B\n",
    "        X = 1;\n",
    "        ... // C\n",
    "        Y = 3;\n",
    "        ... // D\n",
    "        X = 2;\n",
    "        ... // E\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假如在`C`和`D`代码片段中，根本没有用到`X = 1`，那么编译器很可能会将`X = 1`和`X = 2`进行合并:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "unsafe {\n",
    "    ... // B\n",
    "    X = 2;\n",
    "    ... // C\n",
    "    Y = 3;\n",
    "    ... // D\n",
    "    ... // E\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "若代码`A`中创建了一个新的线程用于读取全局静态变量`X`，则该线程将无法读取到`X = 1`的结果，因为在编译阶段就已经被优化掉。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [CPU 缓存导致的内存顺序的改变](https://course.rs/advance/concurrency-with-threads/sync2.html#cpu-缓存导致的内存顺序的改变)\n",
    "\n",
    "假设之前的`X = 1`没有被优化掉，并且在代码片段`A`中有一个新的线程:\n",
    "\n",
    "```console\n",
    "initial state: X = 0, Y = 1\n",
    "\n",
    "THREAD Main     THREAD A\n",
    "X = 1;          if X == 1 {\n",
    "Y = 3;              Y *= 2;\n",
    "X = 2;          }\n",
    "```\n",
    "\n",
    "我们来讨论下以上线程状态，`Y`最终的可能值(可能性依次降低):\n",
    "\n",
    "- `Y = 3`: 线程`Main`运行完后才运行线程`A`，或者线程`A`运行完后再运行线程`Main`\n",
    "- `Y = 6`: 线程`Main`的`Y = 3`运行完，但`X = 2`还没被运行， 此时线程 A 开始运行`Y *= 2`, 最后才运行`Main`线程的`X = 2`\n",
    "- `Y = 2`: 线程`Main`正在运行`Y = 3`还没结束，此时线程`A`正在运行`Y *= 2`, 因此`Y`取到了值 1，然后`Main`的线程将`Y`设置为 3， 紧接着就被线程`A`的`Y = 2`所覆盖\n",
    "- `Y = 2`: 上面的还只是一般的数据竞争，这里虽然产生了相同的结果`2`，但是背后的原理大相径庭: 线程`Main`运行完`Y = 3`，但是 CPU 缓存中的`Y = 3`还没有被同步到其它 CPU 缓存中，此时线程`A`中的`Y *= 2`就开始读取`Y`，结果读到了值`1`，最终计算出结果`2`\n",
    "\n",
    "甚至更改成:\n",
    "\n",
    "```console\n",
    "initial state: X = 0, Y = 1\n",
    "\n",
    "THREAD Main     THREAD A\n",
    "X = 1;          if X == 2 {\n",
    "Y = 3;              Y *= 2;\n",
    "X = 2;          }\n",
    "```\n",
    "\n",
    "还是可能出现`Y = 2`，因为`Main`线程中的`X`和`Y`被同步到其它 CPU 缓存中的顺序未必一致。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [限定内存顺序的 5 个规则](https://course.rs/advance/concurrency-with-threads/sync2.html#限定内存顺序的-5-个规则)\n",
    "\n",
    "在理解了内存顺序可能存在的改变后，你就可以明白为什么 Rust 提供了`Ordering::Relaxed`用于限定内存顺序了，事实上，该枚举有 5 个成员:\n",
    "\n",
    "- **Relaxed**， 这是最宽松的规则，它对编译器和 CPU 不做任何限制，可以乱序\n",
    "- **Release 释放**，设定内存屏障(Memory barrier)，保证它之前的操作永远在它之前，但是它后面的操作可能被重排到它前面\n",
    "- **Acquire 获取**, 设定内存屏障，保证在它之后的访问永远在它之后，但是它之前的操作却有可能被重排到它后面，往往和`Release`在不同线程中联合使用\n",
    "- **AcqRel**, 是 *Acquire* 和 *Release* 的结合，同时拥有它们俩提供的保证。比如你要对一个 `atomic` 自增 1，同时希望该操作之前和之后的读取或写入操作不会被重新排序\n",
    "- **SeqCst 顺序一致性**， `SeqCst`就像是`AcqRel`的加强版，它不管原子操作是属于读取还是写入的操作，只要某个线程有用到`SeqCst`的原子操作，线程中该`SeqCst`操作前的数据操作绝对不会被重新排在该`SeqCst`操作之后，且该`SeqCst`操作后的数据操作也绝对不会被重新排在`SeqCst`操作前。\n",
    "\n",
    "这些规则由于是系统提供的，因此其它语言提供的相应规则也大同小异，大家如果不明白可以看看其它语言的相关解释。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### [内存屏障的例子](https://course.rs/advance/concurrency-with-threads/sync2.html#内存屏障的例子)\n",
    "\n",
    "下面我们以`Release`和`Acquire`为例，使用它们构筑出一对内存屏障，防止编译器和 CPU 将屏障前(Release)和屏障后(Acquire)中的数据操作重新排在屏障围成的范围之外:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::thread::{self, JoinHandle};\n",
    "use std::sync::atomic::{Ordering, AtomicBool};\n",
    "\n",
    "static mut DATA: u64 = 0;\n",
    "static READY: AtomicBool = AtomicBool::new(false);\n",
    "\n",
    "fn reset() {\n",
    "    unsafe {\n",
    "        DATA = 0;\n",
    "    }\n",
    "    READY.store(false, Ordering::Relaxed);\n",
    "}\n",
    "\n",
    "fn producer() -> JoinHandle<()> {\n",
    "    thread::spawn(move || {\n",
    "        unsafe {\n",
    "            DATA = 100;                                 // A\n",
    "        }\n",
    "        READY.store(true, Ordering::Release);           // B: 内存屏障 ↑\n",
    "    })\n",
    "}\n",
    "\n",
    "fn consumer() -> JoinHandle<()> {\n",
    "    thread::spawn(move || {\n",
    "        while !READY.load(Ordering::Acquire) {}         // C: 内存屏障 ↓\n",
    "\n",
    "        assert_eq!(100, unsafe { DATA });               // D\n",
    "    })\n",
    "}\n",
    "\n",
    "\n",
    "fn main() {\n",
    "    loop {\n",
    "        reset();\n",
    "\n",
    "        let t_producer = producer();\n",
    "        let t_consumer = consumer();\n",
    "\n",
    "        t_producer.join().unwrap();\n",
    "        t_consumer.join().unwrap();\n",
    "    }\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "原则上，`Acquire`用于读取，而`Release`用于写入。但是由于有些原子操作同时拥有读取和写入的功能，此时就需要使用`AcqRel`来设置内存顺序了。在内存屏障中被写入的数据，都可以被其它线程读取到，不会有 CPU 缓存的问题。\n",
    "\n",
    "**内存顺序的选择**\n",
    "\n",
    "1. 不知道怎么选择时，优先使用`SeqCst`，虽然会稍微减慢速度，但是慢一点也比出现错误好\n",
    "2. 多线程只计数`fetch_add`而不使用该值触发其他逻辑分支的简单使用场景，可以使用`Relaxed`\n",
    "   参考 [Which std::sync::atomic::Ordering to use?](https://stackoverflow.com/questions/30407121/which-stdsyncatomicordering-to-use)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [多线程中使用 Atomic](https://course.rs/advance/concurrency-with-threads/sync2.html#多线程中使用-atomic)\n",
    "\n",
    "在多线程环境中要使用`Atomic`需要配合`Arc`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::sync::Arc;\n",
    "use std::sync::atomic::{AtomicUsize, Ordering};\n",
    "use std::{hint, thread};\n",
    "\n",
    "fn main() {\n",
    "    let spinlock = Arc::new(AtomicUsize::new(1));\n",
    "\n",
    "    let spinlock_clone = Arc::clone(&spinlock);\n",
    "    let thread = thread::spawn(move|| {\n",
    "        spinlock_clone.store(0, Ordering::SeqCst);\n",
    "    });\n",
    "\n",
    "    // 等待其它线程释放锁\n",
    "    while spinlock.load(Ordering::SeqCst) != 0 {\n",
    "        hint::spin_loop();\n",
    "    }\n",
    "\n",
    "    if let Err(panic) = thread.join() {\n",
    "        println!(\"Thread had an error: {:?}\", panic);\n",
    "    }\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [Atomic 能替代锁吗](https://course.rs/advance/concurrency-with-threads/sync2.html#atomic-能替代锁吗)\n",
    "\n",
    "那么原子类型既然这么全能，它可以替代锁吗？答案是不行：\n",
    "\n",
    "- 对于复杂的场景下，锁的使用简单粗暴，不容易有坑\n",
    "- `std::sync::atomic`包中仅提供了数值类型的原子操作：`AtomicBool`, `AtomicIsize`, `AtomicUsize`, `AtomicI8`, `AtomicU16`等，而锁可以应用于各种类型\n",
    "- 在有些情况下，必须使用锁来配合，例如上一章节中使用`Mutex`配合`Condvar`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### [Atomic 的应用场景](https://course.rs/advance/concurrency-with-threads/sync2.html#atomic-的应用场景)\n",
    "\n",
    "事实上，`Atomic`虽然对于用户不太常用，但是对于高性能库的开发者、标准库开发者都非常常用，它是并发原语的基石，除此之外，还有一些场景适用：\n",
    "\n",
    "- 无锁(lock free)数据结构\n",
    "- 全局变量，例如全局自增 ID, 在后续章节会介绍\n",
    "- 跨线程计数器，例如可以用于统计指标\n",
    "\n",
    "以上列出的只是`Atomic`适用的部分场景，具体场景需要大家未来根据自己的需求进行权衡选择。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基于 Send 和 Sync 的线程安全\n",
    "\n",
    "- https://course.rs/advance/concurrency-with-threads/send-sync.html\n",
    "\n",
    "TL;DR：\n",
    "\n",
    "- **Send**：类型的所有权可安全跨线程移动；`Rc`、裸指针默认不实现，因此不能直接 `move` 到子线程。\n",
    "- **Sync**：类型的引用可安全跨线程共享；若 `&T` 是 `Send`，则 `T` 自动是 `Sync`。\n",
    "- **实现差异**：`Rc` 显式移除了 `Send`/`Sync`，而 `Arc`（在 `T: Send + Sync` 时）实现了两者，因此能在线程间共享。\n",
    "- **常见非线程安全类型**：裸指针、`UnsafeCell` / `Cell` / `RefCell`、`Rc`。\n",
    "- **手动实现**：可通过 newtype 包装（如 `struct MyBox(*mut u8);`）再用 `unsafe impl Send/Sync`，需自行确保线程安全。\n",
    "- **线程共享引用**：组合 `Arc` + 锁（如 `Mutex`）+ `Send/Sync` 类型来安全跨线程共享数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为何 Rc、RefCell 和裸指针不可以在多线程间使用？如何让裸指针可以在多线程使用？我们一起来探寻下这些问题的答案。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [无法用于多线程的`Rc`](https://course.rs/advance/concurrency-with-threads/send-sync.html#无法用于多线程的rc)\n",
    "\n",
    "先来看一段多线程使用`Rc`的代码:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "`Rc<i32>` cannot be sent between threads safely",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0277] Error:\u001b[0m `Rc<i32>` cannot be sent between threads safely",
      "   \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_4:1:1 \u001b[38;5;246m]\u001b[0m",
      "   \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;54m╭\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68md\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68mw\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;100mm\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mv\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m{\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m               \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                     \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m required by a bound introduced by this call",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;100m│\u001b[0m     ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m within this `{closure@src/lib.rs:6:27: 6:34}`",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;37m│\u001b[0m     ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m note: required because it's used within this closure",
      " \u001b[38;5;240m  ┆\u001b[0m \u001b[38;5;54m┆\u001b[0m   ",
      " \u001b[38;5;246m9 │\u001b[0m \u001b[38;5;54m├\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m             ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `Rc<i32>` cannot be sent between threads safely",
      "\u001b[38;5;246m───╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "// https://godbolt.org/z/85nv1PsdP\n",
    "\n",
    "use std::thread;\n",
    "use std::rc::Rc;\n",
    "\n",
    "fn main() {\n",
    "    let v = Rc::new(5);\n",
    "    let t = thread::spawn(move || {\n",
    "        println!(\"{}\",v);\n",
    "    });\n",
    "\n",
    "    t.join().unwrap();\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上代码将`v`的所有权通过`move`转移到子线程中，看似正确实则会报错：\n",
    "\n",
    "表面原因是`Rc`无法在线程间安全的转移，实际是编译器给予我们的那句帮助: `the trait `Send` is not implemented for `Rc<i32>` `(`Rc<i32>`未实现`Send`特征), 那么此处的`Send`特征又是何方神圣？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [Rc 和 Arc 源码对比](https://course.rs/advance/concurrency-with-threads/send-sync.html#rc-和-arc-源码对比)\n",
    "\n",
    "在介绍`Send`特征之前，再来看看`Arc`为何可以在多线程使用，玄机在于两者的源码实现上："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "// Rc源码片段\n",
    "impl<T: ?Sized> !marker::Send for Rc<T> {}\n",
    "impl<T: ?Sized> !marker::Sync for Rc<T> {}\n",
    "\n",
    "// Arc源码片段\n",
    "unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}\n",
    "unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`!`代表移除特征的相应实现，上面代码中`Rc<T>`的`Send`和`Sync`特征被特地移除了实现，而`Arc<T>`则相反，实现了`Sync + Send`，再结合之前的编译器报错，大概可以明白了：`Send`和`Sync`是在线程间安全使用一个值的关键。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [Send 和 Sync](https://course.rs/advance/concurrency-with-threads/send-sync.html#send-和-sync)\n",
    "\n",
    "`Send`和`Sync`是 Rust 安全并发的重中之重，但是实际上它们只是标记特征(marker trait，该特征未定义任何行为，因此非常适合用于标记), 来看看它们的作用：\n",
    "\n",
    "- 实现`Send`的类型可以在线程间安全的传递其所有权\n",
    "- 实现`Sync`的类型可以在线程间安全的共享(通过引用)\n",
    "\n",
    "这里还有一个潜在的依赖：一个类型要在线程间安全的共享的前提是，指向它的引用必须能在线程间传递。因为如果引用都不能被传递，我们就无法在多个线程间使用引用去访问同一个数据了。\n",
    "\n",
    "由上可知，**若类型 T 的引用`&T`是`Send`，则`T`是`Sync`**。\n",
    "\n",
    "没有例子的概念讲解都是耍流氓，来看看`RwLock`的实现:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先`RwLock`可以在线程间安全的共享，那它肯定是实现了`Sync`，但是我们的关注点不在这里。众所周知，`RwLock`可以并发的读，说明其中的值`T`必定也可以在线程间共享，那`T`必定要实现`Sync`。\n",
    "\n",
    "果不其然，上述代码中，`T`的特征约束中就有一个`Sync`特征，那问题又来了，`Mutex`是不是相反？再来看看:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不出所料，`Mutex<T>`中的`T`并没有`Sync`特征约束。\n",
    "\n",
    "武学秘籍再好，不见生死也是花拳绣腿。同样的，我们需要通过实战来彻底掌握`Send`和`Sync`，但在实战之前，先来简单看看有哪些类型实现了它们。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [实现`Send`和`Sync`的类型](https://course.rs/advance/concurrency-with-threads/send-sync.html#实现send和sync的类型)\n",
    "\n",
    "在 Rust 中，几乎所有类型都默认实现了`Send`和`Sync`，而且由于这两个特征都是可自动派生的特征(通过`derive`派生)，意味着一个复合类型(例如结构体), 只要它内部的所有成员都实现了`Send`或者`Sync`，那么它就自动实现了`Send`或`Sync`。\n",
    "\n",
    "正是因为以上规则，Rust 中绝大多数类型都实现了`Send`和`Sync`，除了以下几个(事实上不止这几个，只不过它们比较常见):\n",
    "\n",
    "- 裸指针两者都没实现，因为它本身就没有任何安全保证\n",
    "- `UnsafeCell`不是`Sync`，因此`Cell`和`RefCell`也不是\n",
    "- `Rc`两者都没实现(因为内部的引用计数器不是线程安全的)\n",
    "\n",
    "当然，如果是自定义的复合类型，那没实现那哥俩的就较为常见了：**只要复合类型中有一个成员不是`Send`或`Sync`，那么该复合类型也就不是`Send`或`Sync`**。\n",
    "\n",
    "**手动实现 `Send` 和 `Sync` 是不安全的**，通常并不需要手动实现 Send 和 Sync trait，实现者需要使用`unsafe`小心维护并发安全保证。\n",
    "\n",
    "至此，相关的概念大家已经掌握，但是我敢肯定，对于这两个滑不溜秋的家伙，大家依然会非常模糊，不知道它们该如何使用。那么我们来一起看看如何让裸指针可以在线程间安全的使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [为裸指针实现`Send`](https://course.rs/advance/concurrency-with-threads/send-sync.html#为裸指针实现send)\n",
    "\n",
    "上面我们提到裸指针既没实现`Send`，意味着下面代码会报错:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "`*mut u8` cannot be sent between threads safely",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0277] Error:\u001b[0m `*mut u8` cannot be sent between threads safely",
      "   \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_5:1:1 \u001b[38;5;246m]\u001b[0m",
      "   \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;54m╭\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68md\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68mw\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;100mm\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mv\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m{\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m               \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                     \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m required by a bound introduced by this call",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;100m│\u001b[0m     ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m within this `{closure@src/lib.rs:6:27: 6:34}`",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;37m│\u001b[0m     ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m note: required because it's used within this closure",
      " \u001b[38;5;240m  ┆\u001b[0m \u001b[38;5;54m┆\u001b[0m   ",
      " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;54m├\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m│\u001b[0m             ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `*mut u8` cannot be sent between threads safely",
      "\u001b[38;5;246m───╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::thread;\n",
    "\n",
    "fn main() {\n",
    "    let p = 5 as *mut u8;\n",
    "    let t = thread::spawn(move || {\n",
    "        println!(\"{:?}\",p);\n",
    "    });\n",
    "\n",
    "    t.join().unwrap();\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还记得之前的规则吗：复合类型中有一个成员没实现`Send`，该复合类型就不是`Send`，因此我们需要手动为它实现:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyBox(0x5)\n"
     ]
    }
   ],
   "source": [
    "use std::thread;\n",
    "\n",
    "#[derive(Debug)]\n",
    "struct MyBox(*mut u8);\n",
    "unsafe impl Send for MyBox {}\n",
    "\n",
    "fn main() {\n",
    "    let p = MyBox(5 as *mut u8);\n",
    "    let t = thread::spawn(move || {\n",
    "        println!(\"{:?}\",p);\n",
    "    });\n",
    "\n",
    "    t.join().unwrap();\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此时，我们的指针已经可以欢快的在多线程间撒欢，以上代码很简单，但有一点需要注意：`Send`和`Sync`是`unsafe`特征，实现时需要用`unsafe`代码块包裹。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [为裸指针实现`Sync`](https://course.rs/advance/concurrency-with-threads/send-sync.html#为裸指针实现sync)\n",
    "\n",
    "由于`Sync`是多线程间共享一个值，大家可能会想这么实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "closure may outlive the current function, but it borrows `v`, which is owned by the current function",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0373] Error:\u001b[0m closure may outlive the current function, but it borrows `v`, which is owned by the current function",
      "   \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_2:1:1 \u001b[38;5;246m]\u001b[0m",
      "   \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;68m╭\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m▶\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68md\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68mw\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68m(\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;100m|\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m{\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                             \u001b[38;5;37m┬\u001b[0m\u001b[38;5;100m┬\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                             \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword: `move `",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                              \u001b[38;5;100m│\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                              \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m may outlive borrowed value `v`",
      " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;68m│\u001b[0m   \u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68mp\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68ml\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68m!\u001b[0m\u001b[38;5;68m(\u001b[0m\u001b[38;5;68m\"\u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68m:\u001b[0m\u001b[38;5;68m?\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;68m\"\u001b[0m\u001b[38;5;68m,\u001b[0m\u001b[38;5;68m&\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;68m)\u001b[0m\u001b[38;5;68m;\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                            \u001b[38;5;54m┬\u001b[0m  ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m                            \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `v` is borrowed here",
      " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;68m├\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m▶\u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m \u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;68m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m│\u001b[0m             ",
      " \u001b[38;5;240m  │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m note: function requires argument type to outlive `'static`",
      "\u001b[38;5;246m───╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::thread;\n",
    "\n",
    "fn main() {\n",
    "    let v = 5;\n",
    "    let t = thread::spawn(|| {\n",
    "        println!(\"{:?}\",&v);\n",
    "    });\n",
    "\n",
    "    t.join().unwrap();\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关于这种用法，在多线程章节也提到过，线程如果直接去借用其它线程的变量，会报错:`closure may outlive the current function,`, 原因在于编译器无法确定主线程`main`和子线程`t`谁的生命周期更长，特别是当两个线程都是子线程时，没有任何人知道哪个子线程会先结束，包括编译器！\n",
    "\n",
    "因此我们得配合`Arc`去使用:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [
    {
     "ename": "Error",
     "evalue": "`*const u8` cannot be shared between threads safely",
     "output_type": "error",
     "traceback": [
      "\u001b[31m[E0277] Error:\u001b[0m `*const u8` cannot be shared between threads safely",
      "    \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_3:1:1 \u001b[38;5;246m]\u001b[0m",
      "    \u001b[38;5;246m│\u001b[0m",
      " \u001b[38;5;246m 6 │\u001b[0m     \u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mM\u001b[0m\u001b[38;5;68my\u001b[0m\u001b[38;5;68mB\u001b[0m\u001b[38;5;68mo\u001b[0m\u001b[38;5;68mx\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m*\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249m8\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m            \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m              \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m note: required because it appears within the type `MyBox`",
      " \u001b[38;5;240m   │\u001b[0m ",
      " \u001b[38;5;246m12 │\u001b[0m \u001b[38;5;54m╭\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100mh\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100mw\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;37mm\u001b[0m\u001b[38;5;37mo\u001b[0m\u001b[38;5;37mv\u001b[0m\u001b[38;5;37me\u001b[0m\u001b[38;5;37m \u001b[0m\u001b[38;5;37m|\u001b[0m\u001b[38;5;37m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m{\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m               \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m┬\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m  ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                     \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m required by a bound introduced by this call",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;37m│\u001b[0m     ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m                                \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m note: required because it's used within this closure",
      " \u001b[38;5;240m   ┆\u001b[0m \u001b[38;5;54m┆\u001b[0m   ",
      " \u001b[38;5;246m14 │\u001b[0m \u001b[38;5;54m├\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m│\u001b[0m             ",
      " \u001b[38;5;240m   │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `*const u8` cannot be shared between threads safely",
      "\u001b[38;5;246m────╯\u001b[0m"
     ]
    }
   ],
   "source": [
    "use std::thread;\n",
    "use std::sync::Arc;\n",
    "use std::sync::Mutex;\n",
    "\n",
    "#[derive(Debug)]\n",
    "struct MyBox(*const u8);\n",
    "unsafe impl Send for MyBox {}\n",
    "\n",
    "fn main() {\n",
    "    let b = &MyBox(5 as *const u8);\n",
    "    let v = Arc::new(Mutex::new(b));\n",
    "    let t = thread::spawn(move || {\n",
    "        let _v1 =  v.lock().unwrap();\n",
    "    });\n",
    "\n",
    "    t.join().unwrap();\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码将智能指针`v`的所有权转移给新线程，同时`v`包含了一个引用类型`b`，当在新的线程中试图获取内部的引用时，会报错：\n",
    "\n",
    "因为我们访问的引用实际上还是对主线程中的数据的借用，转移进来的仅仅是外层的智能指针引用。要解决很简单，为`MyBox`实现`Sync`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "vscode": {
     "languageId": "rust"
    }
   },
   "outputs": [],
   "source": [
    "use std::thread;\n",
    "use std::sync::Arc;\n",
    "use std::sync::Mutex;\n",
    "\n",
    "#[derive(Debug)]\n",
    "struct MyBox(*const u8);\n",
    "unsafe impl Send for MyBox {}\n",
    "unsafe impl Sync for MyBox {}\n",
    "\n",
    "fn main() {\n",
    "    let b = &MyBox(5 as *const u8);\n",
    "    let v = Arc::new(Mutex::new(b));\n",
    "    let t = thread::spawn(move || {\n",
    "        let _v1 =  v.lock().unwrap();\n",
    "    });\n",
    "\n",
    "    t.join().unwrap();\n",
    "}\n",
    "\n",
    "main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [总结](https://course.rs/advance/concurrency-with-threads/send-sync.html#总结)\n",
    "\n",
    "通过上面的两个裸指针的例子，我们了解了如何实现`Send`和`Sync`，以及如何只实现`Send`而不实现`Sync`，简单总结下：\n",
    "\n",
    "1. 实现`Send`的类型可以在线程间安全的传递其所有权, 实现`Sync`的类型可以在线程间安全的共享(通过引用)\n",
    "2. 绝大部分类型都实现了`Send`和`Sync`，常见的未实现的有：裸指针、`Cell`、`RefCell`、`Rc` 等\n",
    "3. 可以为自定义类型实现`Send`和`Sync`，但是需要`unsafe`代码块\n",
    "4. 可以为部分 Rust 中的类型实现`Send`、`Sync`，但是需要使用`newtype`，例如文中的裸指针例子"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Rust",
   "language": "rust",
   "name": "rust"
  },
  "language_info": {
   "codemirror_mode": "rust",
   "file_extension": ".rs",
   "mimetype": "text/rust",
   "name": "Rust",
   "pygment_lexer": "rust",
   "version": ""
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
